Make the "Preferences" dialog box use the new utilities to make the Esc
[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.34 2000/04/18 04:46:06 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 "conversation.h"
45 #include "packet-ipx.h"
46 #include "packet-ncp.h"
47
48 static int proto_ncp = -1;
49 static int hf_ncp_ip_ver = -1;
50 static int hf_ncp_ip_sig = -1;
51 static int hf_ncp_type = -1;
52 static int hf_ncp_seq = -1;
53 static int hf_ncp_connection = -1;
54 static int hf_ncp_task = -1;
55
56 static gint ett_ncp = -1;
57 static gint ett_ncp_request_fields = -1;
58 static gint ett_ncp_reply_fields = -1;
59
60 #define TCP_PORT_NCP            524
61 #define UDP_PORT_NCP            524
62
63 struct svc_record;
64
65 static void
66 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
67         guint16 nw_connection, guint8 nw_sequence, guint16 nw_ncp_type,
68         proto_tree *ncp_tree, proto_tree *tree);
69
70 static void
71 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
72         guint16 nw_connection, guint8 nw_sequence,
73         proto_tree *ncp_tree, proto_tree *tree);
74
75 static struct ncp2222_record *
76 ncp2222_find(guint8 func, guint8 subfunc);
77
78 static void
79 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
80         struct svc_record *svc);
81
82
83 /* Hash functions */
84 gint  ncp_equal (gconstpointer v, gconstpointer v2);
85 guint ncp_hash  (gconstpointer v);
86
87 int ncp_packet_init_count = 200;
88
89 /* These are the header structures to handle NCP over IP */
90 #define NCPIP_RQST      0x446d6454      /* "DmdT" */
91 #define NCPIP_RPLY      0x744e6350      /* "tNcP" */
92
93 struct ncp_ip_header {
94         guint32 signature;
95         guint32 length;
96 };
97
98 /* This header only appears on NCP over IP request packets */
99 struct ncp_ip_rqhdr {
100         guint32 version;
101         guint32 rplybufsize;
102 };
103
104 static const value_string ncp_ip_signature[] = {
105         { NCPIP_RQST, "Demand Transport (Request)" },
106         { NCPIP_RPLY, "Transport is NCP (Reply)" },
107 };
108
109 /* The information in this module comes from:
110         NetWare LAN Analysis, Second Edition
111         Laura A. Chappell and Dan E. Hakes
112         (c) 1994 Novell, Inc.
113         Novell Press, San Jose.
114         ISBN: 0-7821-1362-1
115
116   And from the ncpfs source code by Volker Lendecke
117
118   And:
119         Programmer's Guide to the NetWare Core Protocol
120         Steve Conner & Diane Conner
121         (c) 1996 by Steve Conner & Diane Conner
122         Published by Annabooks, San Diego, California
123         ISBN: 0-929392-31-0
124
125 */
126
127 /* Every NCP packet has this common header */
128 struct ncp_common_header {
129         guint16 type;
130         guint8  sequence;
131         guint8  conn_low;
132         guint8  task;
133         guint8  conn_high;
134 };
135
136 /* NCP request packets */
137 struct ncp_request_header {
138         guint16 type;
139         guint8  sequence;
140         guint8  conn_low;
141         guint8  task;
142         guint8  conn_high;
143         guint8  function;
144         guint16 length;
145         guint8  subfunc;
146 };
147
148 /* NCP reply packets */
149 struct ncp_reply_header {
150         guint16 type;
151         guint8  sequence;
152         guint8  conn_low;
153         guint8  task;
154         guint8  conn_high;
155         guint8  completion_code;
156         guint8  connection_state;
157 };
158
159
160 static value_string request_reply_values[] = {
161         { 0x1111, "Create a service connection" },
162         { 0x2222, "Service request" },
163         { 0x3333, "Service reply" },
164         { 0x5555, "Destroy service connection" },
165         { 0x7777, "Burst mode transfer" },
166         { 0x9999, "Request being processed" },
167         { 0x0000, NULL }
168 };
169
170 /* These are the field types in an NCP packet */
171 enum ntype {
172         nend,           /* end of the NCP field list */
173         nbyte,          /* one byte of data */
174         nhex,           /* bytes to be shown as hex digits */
175         nbelong,        /* 4-byte big-endian long int */
176         nbeshort,       /* 2-byte big-endian short int */
177         ndata,          /* unstructured data */
178         nbytevar,       /* a variable number of bytes */
179         ndatetime,      /* date-time stamp */
180         nasciile,       /* length-encoded ASCII string. First byte is length */
181         nasciiz         /* null-terminated string of ASCII characters */
182 };
183
184 /* These are the broad families that the different NCP request types belong
185  * to.
186  */
187 enum nfamily {
188                 NCP_UNKNOWN_SERVICE,    /* unknown or n/a */
189                 NCP_QUEUE_SERVICES,             /* print queues */
190                 NCP_FILE_SERVICES,              /* file serving */
191                 NCP_BINDERY_SERVICES,   /* bindery database */
192                 NCP_CONNECTION_SERVICES /* communication */
193 };
194
195 /* I had to put this function prototype after the enum nfamily declaration */
196 static char*
197 ncp_completion_code(guint8 ccode, enum nfamily family);
198
199
200 /* Information on the NCP field */
201 typedef struct svc_record {
202         enum ntype      type;
203         guint8          length; /* max-length for variable-sized fields */
204         gchar           *description;
205 } svc_record;
206
207 typedef struct ncp2222_record {
208         guint8          func;
209         guint8          subfunc;
210         guint8          submask;        /* Does this function have subfunctions?
211                                          * SUBFUNC or NOSUB */
212         gchar           *funcname;
213
214         svc_record      *req;
215         svc_record      *rep;
216         enum nfamily    family;
217
218 } ncp2222_record;
219
220
221 /* ------------------------------------------------------------ */
222
223 /* Get Bindery Object ID REQUEST */
224 static svc_record ncp_17_35_C[] = {
225                 { nbeshort,     2,      "Object Type: 0x%04x" },
226                 { nasciile,     48,     "Object Name: %.*s" },
227                 { nend,         0,      NULL }
228 };
229 /* Get Bindery Object ID REPLY has no fields*/
230
231
232 /* Service Queue Job REQUEST */
233 static svc_record ncp_17_7C_C[] = {
234                 { nbelong,      4,      "The queue the job resides in" },
235                 { nbeshort,     2,      "Job Type" },
236                 { nend,         0,      NULL }
237 };
238 /* Service Queue Job REPLY */
239 static svc_record ncp_17_7C_R[] = {
240                 { nbelong,      4,      "Client station number: %d" },
241                 { nbelong,      4,      "Task Number: %d" },
242                 { nbelong,      4,      "User: %d" },
243                 { nbelong,      4,      "Server specifed to service queue entry: %08X" },
244                 { ndatetime,    6,      "Earliest time to execute" },
245                 { ndatetime,    6,      "When job entered queue" },
246                 { nbelong,      4,      "Job Number" },
247                 { nbeshort,     2,      "Job Type" },
248                 { nbeshort,     2,      "Job Position" },
249                 { nbeshort,     2,      "Current status of job: 0x%02x" },
250                 { nasciiz,      14,     "Name of file" },
251                 { nbelong,      4,      "File handle" },
252                 { nbelong,      4,      "Client station number" },
253                 { nbelong,      4,      "Task number" },
254                 { nbelong,      4,      "Job server" },
255                 { nend,         0,      NULL }
256 };
257
258
259
260 /* Negotiate Buffer Size REQUEST */
261 static svc_record ncp_21_00_C[] = {
262                 { nbeshort,     2,      "Caller's maximum packet size: %d bytes" },
263                 { nend,         0,      NULL }
264 };
265 /* Negotiate Buffer Size RESPONSE */
266 static svc_record ncp_21_00_R[] = {
267                 { nbeshort,     2,      "Packet size decided upon by file server: %d bytes" },
268                 { nend,         0,      NULL }
269 };
270
271
272 /* Close File REQUEST */
273 static svc_record ncp_42_00_C[] = {
274                 { nhex,         6,      "File Handle: 02x:02x:02x:02x:02x:02x"},
275                 { nend,         0,      NULL }
276 };
277 /* Close File RESPONSE */
278 static svc_record ncp_42_00_R[] = {
279                 { nend,         0,      NULL }
280 };
281
282
283 /* Read from a file REQUEST */
284 static svc_record ncp_48_00_C[] = {
285                 { nbyte,        1,      "Unknown" },
286                 { nhex,         6,      "File Handle" },
287                 { nbelong,      4,      "Byte offset within file" },
288                 { nbeshort,     2,      "Maximum data bytes to return" },
289                 { nend,         0,      NULL }
290 };
291 /* RESPONSE */
292 static svc_record ncp_48_00_R[] = {
293                 { nbeshort,     2,      "Data bytes returned" },
294                 { nbytevar,     1,      "Padding" },
295                 { ndata,        0,      NULL }
296 };
297
298 /* ------------------------------------------------------------ */
299 /* Any svc_record that has no fields is not created.
300  *  Store a NULL in the ncp2222_record instead */
301
302 #define SUBFUNC 0xff
303 #define NOSUB   0x00
304
305 static ncp2222_record ncp2222[] = {
306
307 { 0x00, 0x00, NOSUB, "Create service connection",
308         NULL, NULL, NCP_CONNECTION_SERVICES
309 },
310
311 { 0x14, 0x00, NOSUB, "Get server's clock",
312         NULL, NULL, NCP_FILE_SERVICES
313 },
314
315 { 0x16, 0x01, SUBFUNC, "Get path of directory handle",
316         NULL, NULL, NCP_FILE_SERVICES
317 },
318
319 { 0x16, 0x13, SUBFUNC, "Create temporary directory handle",
320         NULL, NULL, NCP_BINDERY_SERVICES
321 },
322
323 { 0x16, 0x0A, SUBFUNC, "Create directory",
324         NULL, NULL, NCP_BINDERY_SERVICES
325 },
326
327 { 0x16, 0x0D, SUBFUNC, "Add trustee to directory",
328         NULL, NULL, NCP_BINDERY_SERVICES
329 },
330
331 { 0x17, 0x11, SUBFUNC, "Get fileserver information",
332         NULL, NULL, NCP_BINDERY_SERVICES
333 },
334
335 { 0x17, 0x37, SUBFUNC, "Scan bindery object",
336         NULL, NULL, NCP_BINDERY_SERVICES
337 },
338
339 { 0x17, 0x36, SUBFUNC, "Get bindery object name",
340         NULL, NULL, NCP_BINDERY_SERVICES
341 },
342
343 { 0x17, 0x32, SUBFUNC, "Create bindery object",
344         NULL, NULL, NCP_BINDERY_SERVICES
345 },
346
347 { 0x17, 0x39, SUBFUNC, "Create property",
348         NULL, NULL, NCP_BINDERY_SERVICES
349 },
350
351 { 0x17, 0x41, SUBFUNC, "Add bindery object to set",
352         NULL, NULL, NCP_BINDERY_SERVICES
353 },
354
355 { 0x17, 0x43, SUBFUNC, "Is bindery object in set",
356         NULL, NULL, NCP_BINDERY_SERVICES
357 },
358
359 { 0x17, 0x35, SUBFUNC, "Get Bindery Object ID",
360         ncp_17_35_C, NULL, NCP_BINDERY_SERVICES
361 },
362
363 { 0x17, 0x7C, SUBFUNC, "Service Queue Job",
364         ncp_17_7C_C, ncp_17_7C_R, NCP_QUEUE_SERVICES
365 },
366
367 { 0x17, 0x3D, SUBFUNC, "Read property value",
368         NULL, NULL, NCP_FILE_SERVICES
369 },
370
371 { 0x18, 0x00, NOSUB, "End of Job",
372         NULL, NULL, NCP_CONNECTION_SERVICES
373 },
374
375 { 0x19, 0x00, NOSUB, "Logout",
376         NULL, NULL, NCP_CONNECTION_SERVICES
377 },
378
379 { 0x21, 0x00, NOSUB, "Negotiate Buffer Size",
380         ncp_21_00_C, ncp_21_00_R, NCP_CONNECTION_SERVICES
381 },
382
383 { 0x24, 0x00, SUBFUNC, "Destroy service connection",
384         NULL, NULL, NCP_CONNECTION_SERVICES
385 },
386
387 { 0x3E, 0x53, SUBFUNC, "Get alternate directory search paths",
388         NULL, NULL, NCP_FILE_SERVICES
389 },
390
391 { 0x3F, 0x89, SUBFUNC, "File search continue",
392         NULL, NULL, NCP_FILE_SERVICES
393 },
394
395 { 0x42, 0x00, NOSUB, "Close File",
396         ncp_42_00_C, ncp_42_00_R, NCP_FILE_SERVICES
397 },
398
399 { 0x48, 0x00, NOSUB, "Read from a file",
400         ncp_48_00_C, ncp_48_00_R, NCP_FILE_SERVICES
401 },
402
403 { 0x4C, 0x11, SUBFUNC, "Open file",
404         NULL, NULL, NCP_FILE_SERVICES
405 },
406
407 { 0x61, 0x00, NOSUB, "Get big packet NCP max packet size",
408         NULL, NULL, NCP_FILE_SERVICES
409 },
410
411 { 0x68, 0x01, SUBFUNC, "Ping for NDS NCP",
412         NULL, NULL, NCP_FILE_SERVICES
413 },
414
415 { 0x68, 0x02, SUBFUNC, "Send NDS fragmented message",
416         NULL, NULL, NCP_FILE_SERVICES
417 },
418
419 { 0x00, 0x00, NOSUB, NULL,
420         NULL, NULL, NCP_UNKNOWN_SERVICE
421 }
422
423 };
424
425
426 /* NCP packets come in request/reply pairs. The request packets tell the type
427  * of NCP request and give a sequence ID. The response, unfortunately, only
428  * identifies itself via the sequence ID; you have to know what type of NCP
429  * request the request packet contained in order to successfully parse the NCP
430  * response. A global method for doing this does not exist in ethereal yet
431  * (NFS also requires it), so for now the NCP section will keep its own hash
432  * table keeping track of NCP packet types.
433  *
434  * We construct a conversation specified by the client and server
435  * addresses and the connection number; the key representing the unique
436  * NCP request then is composed of the pointer to the conversation
437  * structure, cast to a "guint" (which may throw away the upper 32
438  * bits of the pointer on a P64 platform, but the low-order 32 bits
439  * are more likely to differ between conversations than the upper 32 bits),
440  * and the sequence number.
441  *
442  * The value stored in the hash table is the ncp_request_val pointer. This
443  * struct tells us the NCP type and gives the ncp2222_record pointer, if
444  * ncp_type == 0x2222.
445  */
446
447 struct ncp_request_key {
448         conversation_t  *conversation;
449         guint8          nw_sequence;
450 };
451
452 struct ncp_request_val {
453         guint32                 ncp_type;
454         struct ncp2222_record*  ncp_record;
455 };
456
457 static GHashTable *ncp_request_hash = NULL;
458 static GMemChunk *ncp_request_keys = NULL;
459 static GMemChunk *ncp_request_records = NULL;
460
461 /* Hash Functions */
462 gint  ncp_equal (gconstpointer v, gconstpointer v2)
463 {
464         struct ncp_request_key  *val1 = (struct ncp_request_key*)v;
465         struct ncp_request_key  *val2 = (struct ncp_request_key*)v2;
466
467         #if defined(DEBUG_NCP_HASH)
468         printf("Comparing %p:%d and %p:%d\n",
469                 val1->conversation, val1->nw_sequence,
470                 val2->conversation, val2->nw_sequence);
471         #endif
472
473         if (val1->conversation == val2->conversation &&
474             val1->nw_sequence  == val2->nw_sequence ) {
475                 return 1;
476         }
477         return 0;
478 }
479
480 guint ncp_hash  (gconstpointer v)
481 {
482         struct ncp_request_key  *ncp_key = (struct ncp_request_key*)v;
483 #if defined(DEBUG_NCP_HASH)
484         printf("hash calculated as %u\n",
485                 GPOINTER_TO_UINT(ncp_key->conversation) + ncp_key->nw_sequence);
486 #endif
487         return GPOINTER_TO_UINT(ncp_key->conversation) + ncp_key->nw_sequence;
488 }
489
490 /* Initializes the hash table and the mem_chunk area each time a new
491  * file is loaded or re-loaded in ethereal */
492 static void
493 ncp_init_protocol(void)
494 {
495         #if defined(DEBUG_NCP_HASH)
496         printf("Initializing NCP hashtable and mem_chunk area\n");
497         #endif
498         if (ncp_request_hash)
499                 g_hash_table_destroy(ncp_request_hash);
500         if (ncp_request_keys)
501                 g_mem_chunk_destroy(ncp_request_keys);
502         if (ncp_request_records)
503                 g_mem_chunk_destroy(ncp_request_records);
504
505         ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal);
506         ncp_request_keys = g_mem_chunk_new("ncp_request_keys",
507                         sizeof(struct ncp_request_key),
508                         ncp_packet_init_count * sizeof(struct ncp_request_key), G_ALLOC_AND_FREE);
509         ncp_request_records = g_mem_chunk_new("ncp_request_records",
510                         sizeof(struct ncp_request_val),
511                         ncp_packet_init_count * sizeof(struct ncp_request_val), G_ALLOC_AND_FREE);
512 }
513
514 static struct ncp2222_record *
515 ncp2222_find(guint8 func, guint8 subfunc)
516 {
517         struct ncp2222_record *ncp_record, *retval = NULL;
518
519         ncp_record = ncp2222;
520
521         while(ncp_record->func != 0 || ncp_record->subfunc != 0 ||
522                 ncp_record->funcname != NULL ) {
523                 if (ncp_record->func == func &&
524                         ncp_record->subfunc == (subfunc & ncp_record->submask)) {
525                         retval = ncp_record;
526                         break;
527                 }
528                 ncp_record++;
529         }
530
531         return retval;
532 }
533
534 void
535 dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
536
537         proto_tree      *ncp_tree = NULL;
538         proto_item      *ti;
539         int             ncp_hdr_length = 0;
540         struct ncp_ip_header            ncpiph;
541         struct ncp_ip_rqhdr             ncpiphrq;
542         struct ncp_common_header        header;
543         guint16         nw_connection;
544         guint8          nw_sequence;
545         guint16         nw_ncp_type;
546
547         if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
548                 ncpiph.signature = pntohl(&pd[offset]);
549                 ncpiph.length = pntohl(&pd[offset + 4]);
550                 offset += 8;
551                 if ( ncpiph.signature == NCPIP_RQST ) {
552                         ncpiphrq.version = pntohl(&pd[offset]);
553                         ncpiphrq.rplybufsize = pntohl(&pd[offset + 4]);
554                         offset += 8;
555                 };
556         };
557
558         memcpy(&header, &pd[offset], sizeof(header));
559         header.type = ntohs(header.type);
560
561         if (header.type == 0x1111 ||
562                         header.type == 0x2222 ||
563                         header.type == 0x5555 ||
564                         header.type == 0x7777) {
565                 ncp_hdr_length = 7;
566         }
567         else if (header.type == 0x3333 || header.type == 0x9999) {
568                 ncp_hdr_length = 8;
569         }
570
571         if (check_col(fd, COL_PROTOCOL))
572                 col_add_str(fd, COL_PROTOCOL, "NCP");
573
574         nw_connection = (header.conn_high << 16) + header.conn_low;
575         nw_sequence = header.sequence;
576         nw_ncp_type = header.type;
577
578         if (tree) {
579                 ti = proto_tree_add_item(tree, proto_ncp, offset, END_OF_FRAME, NULL);
580                 ncp_tree = proto_item_add_subtree(ti, ett_ncp);
581
582                 if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
583                         proto_tree_add_item(ncp_tree, hf_ncp_ip_sig, offset - 16, 4, ncpiph.signature);
584                         proto_tree_add_text(ncp_tree, offset - 12, 4, "Length: %d", ncpiph.length);
585                         if ( ncpiph.signature == NCPIP_RQST ) {
586                                 proto_tree_add_item(ncp_tree, hf_ncp_ip_ver, offset - 8, 4, ncpiphrq.version);
587                                 proto_tree_add_text(ncp_tree, offset - 4, 4, "Reply buffer size: %d", ncpiphrq.rplybufsize);
588                         };
589                 };
590                 proto_tree_add_uint_format(ncp_tree, hf_ncp_type, 
591                                            offset,      2,
592                                            header.type,
593                                            "Type: %s", 
594                                            val_to_str( header.type,
595                                                        request_reply_values,
596                                                        "Unknown (%04X)"));
597
598                 proto_tree_add_item(ncp_tree, hf_ncp_seq, 
599                                     offset+2,    1, header.sequence);
600
601                 proto_tree_add_item(ncp_tree, hf_ncp_connection,
602                                     offset+3,    3, nw_connection);
603
604                 proto_tree_add_item(ncp_tree, hf_ncp_task, 
605                                     offset+4,    1, header.task);
606         }
607
608         /* Note how I use ncp_tree *and* tree in my args for ncp request/reply */
609         if (ncp_hdr_length == 7)
610                 dissect_ncp_request(pd, offset, fd, nw_connection,
611                                 nw_sequence, nw_ncp_type, ncp_tree, tree);
612         else if (ncp_hdr_length == 8)
613                 dissect_ncp_reply(pd, offset, fd, nw_connection,
614                                 nw_sequence, ncp_tree, tree);
615         else
616                 dissect_data(pd, offset, fd, tree);
617 }
618
619 static void
620 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
621         guint16 nw_connection, guint8 nw_sequence, guint16 nw_ncp_type,
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         conversation_t                  *conversation;
628         struct ncp_request_val          *request_val;
629         struct ncp_request_key          *request_key;
630         proto_tree                      *field_tree = NULL;
631         proto_item                      *ti = NULL;
632
633         /*memcpy(&request, &pd[offset], sizeof(request));*/
634         request.function = pd[offset+6];
635         if ( BYTES_ARE_IN_FRAME(offset, 9) ) {
636                 request.subfunc = pd[offset+9];
637         } else {
638                 request.subfunc = 0;
639         }
640
641         ncp_request = ncp2222_find(request.function, request.subfunc);
642
643         if (ncp_request)
644                 description = ncp_request->funcname;
645
646         if (check_col(fd, COL_INFO)) {
647                 if (description[0]) {
648                         col_add_fstr(fd, COL_INFO, "C %s", description);
649                 }
650                 else {
651                         col_add_fstr(fd, COL_INFO, "C Unknown Function %02X/%02X",
652                                 request.function, request.subfunc);
653                 }
654         }
655
656         if (!fd->flags.visited) {
657                 /* This is the first time we've looked at this packet.
658                    Keep track of the address and connection whence the request
659                    came, and the address and connection to which the request
660                    is being sent, so that we can match up calls with replies.
661                    (We don't include the sequence number, as we may want
662                    to have all packets over the same connection treated
663                    as being part of a single conversation so that we can
664                    let the user select that conversation to be displayed.) */
665                 conversation = find_conversation(&pi.src, &pi.dst,
666                     PT_NCP, nw_connection, nw_connection);
667                 if (conversation == NULL) {
668                         /* It's not part of any conversation - create a new one. */
669                         conversation = conversation_new(&pi.src, &pi.dst,
670                             PT_NCP, nw_connection, nw_connection, NULL);
671                 }
672
673                 /* Now remember the request, so we can find it if we later
674                    a reply to it. */
675                 request_key = g_mem_chunk_alloc(ncp_request_keys);
676                 request_key->conversation = conversation;
677                 request_key->nw_sequence = nw_sequence;
678
679                 request_val = g_mem_chunk_alloc(ncp_request_records);
680                 request_val->ncp_type = nw_ncp_type;
681                 request_val->ncp_record = ncp2222_find(request.function, request.subfunc);
682
683                 g_hash_table_insert(ncp_request_hash, request_key, request_val);
684                 #if defined(DEBUG_NCP_HASH)
685                 printf("Inserted conversation %p sequence %d (val=%p)\n",
686                         conversation, nw_sequence, request_val);
687                 #endif
688         }
689
690         if (ncp_tree) {
691                 proto_tree_add_text(ncp_tree, offset+6, 1,
692                         "Function Code: 0x%02X (%s)",
693                         request.function, description);
694
695                 if (ncp_request) {
696
697                         if (ncp_request->submask == SUBFUNC) {
698                                 proto_tree_add_text(ncp_tree, offset+7, 2,
699                                         "Packet Length: %d bytes", pntohs(&pd[offset+7]));
700                                 proto_tree_add_text(ncp_tree, offset+9, 1,
701                                         "Subfunction Code: 0x%02x", pd[offset+9]);
702                                 offset += 7 + 3;
703                         }
704                         else {
705                                 offset += 7;
706                         }
707
708                         if (ncp_request->req) {
709                                 ti = proto_tree_add_text(ncp_tree, offset, END_OF_FRAME,
710                                 "NCP Request Packet");
711                                 field_tree = proto_item_add_subtree(ti, ett_ncp_request_fields);
712
713                                 parse_ncp_svc_fields(pd, field_tree, offset, ncp_request->req);
714                         }
715                 }
716         }
717 }
718
719 static void
720 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
721         guint16 nw_connection, guint8 nw_sequence,
722         proto_tree *ncp_tree, proto_tree *tree) {
723
724         struct ncp_reply_header         reply;
725         conversation_t                  *conversation;
726         struct ncp2222_record           *ncp_request = NULL;
727         struct ncp_request_val          *request_val;
728         struct ncp_request_key          request_key;
729         proto_tree                      *field_tree = NULL;
730         proto_item                      *ti = NULL;
731
732         memcpy(&reply, &pd[offset], sizeof(reply));
733
734         /* Find the conversation whence the request would have come. */
735
736         conversation = find_conversation(&pi.src, &pi.dst,
737                     PT_NCP, nw_connection, nw_connection);
738         if (conversation != NULL) {
739                 /* find the record telling us the request made that caused
740                    this reply */
741                 request_key.conversation = conversation;
742                 request_key.nw_sequence = nw_sequence;
743
744                 #if defined(DEBUG_NCP_HASH)
745                 printf("Looking for conversation %p sequence %u (retval=%p)\n",
746                         conversation, nw_sequence, request_val);
747                 #endif
748
749                 request_val = (struct ncp_request_val*)
750                         g_hash_table_lookup(ncp_request_hash, &request_key);
751         } else {
752                 /* We haven't seen an RPC call for that conversation,
753                    so we can't check for a reply to that call. */
754                 request_val = NULL;
755         }
756
757         if (request_val)
758                 ncp_request = request_val->ncp_record;
759
760         if (check_col(fd, COL_INFO)) {
761                 if (reply.completion_code == 0) {
762                         col_add_fstr(fd, COL_INFO, "R OK");
763                 }
764                 else {
765                         col_add_fstr(fd, COL_INFO, "R Not OK");
766                 }
767         }
768
769         if (ncp_tree) {
770                 /* A completion code of 0 always means OK. Other values have different
771                  * meanings */
772                 if (ncp_request) {
773                         proto_tree_add_text(ncp_tree, offset+6,    1,
774                                 "Completion Code: 0x%02x (%s)", reply.completion_code,
775                                 ncp_completion_code(reply.completion_code, ncp_request->family));
776                 }
777                 else {
778                         proto_tree_add_text(ncp_tree, offset+6,    1,
779                                 "Completion Code: 0x%02x (%s)", reply.completion_code,
780                                 reply.completion_code == 0 ? "OK" : "Unknown");
781                 }
782
783                 proto_tree_add_text(ncp_tree, offset+7,    1,
784                         "Connection Status: %d", reply.connection_state);
785
786                 if (ncp_request) {
787
788                         if (ncp_request->rep) {
789                                 ti = proto_tree_add_text(ncp_tree, offset+8, END_OF_FRAME,
790                                 "NCP Reply Packet");
791                                 field_tree = proto_item_add_subtree(ti, ett_ncp_reply_fields);
792
793                                 parse_ncp_svc_fields(pd, field_tree, offset+8, ncp_request->rep);
794                         }
795                 }
796         }
797
798 }
799
800 /* Populates the protocol tree with information about the svc_record fields */
801 static void
802 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
803         struct svc_record *svc)
804 {
805         struct svc_record *rec = svc;
806         int field_offset = offset;
807         int field_length = 0;
808
809         while (rec->type != nend) {
810                 switch(rec->type) {
811                         case nbeshort:
812                                 field_length = 2;
813                                 proto_tree_add_text(ncp_tree, field_offset,
814                                         field_length, rec->description, pntohs(&pd[field_offset]));
815                                 break;
816
817                         case nasciile:
818                                 field_length = pd[field_offset];
819                                 proto_tree_add_text(ncp_tree, field_offset,
820                                         field_length + 1, rec->description, field_length,
821                                         &pd[field_offset+1]);
822                                 break;
823
824                         case nhex:
825                                 field_length = rec->length;
826                                 proto_tree_add_text(ncp_tree, field_offset,
827                                         field_length, rec->description);
828                                 break;  
829
830                          default:
831                                 ; /* nothing */
832                                 break;
833                 }
834                 field_offset += field_length;
835                 rec++;
836         }       
837 }
838
839 static char*
840 ncp_completion_code(guint8 ccode, enum nfamily family)
841 {
842                 char    *text;
843
844 #define NCP_CCODE_MIN 0x7e
845 #define NCP_CCODE_MAX 0xff
846
847         /* From Appendix C of "Programmer's Guide to NetWare Core Protocol" */
848         static char     *ccode_text[] = {
849                 /* 7e */ "NCP boundary check failed",
850                 /* 7f */ "Unknown",
851                 /* 80 */ "Lock fail. The file is already open",
852                 /* 81 */ "A file handle could not be allocated by the file server",
853                 /* 82 */ "Unauthorized to open file",
854                 /* 83 */ "Unable to read/write the volume. Possible bad sector on the file server",
855                 /* 84 */ "Unauthorized to create the file",
856                 /* 85 */ "",
857                 /* 86 */ "Unknown",
858                 /* 87 */ "An unexpected character was encountered in the filename",
859                 /* 88 */ "FileHandle is not valid",
860                 /* 89 */ "Unauthorized to search this directory",
861                 /* 8a */ "Unauthorized to delete a file in this directory",
862                 /* 8b */ "Unauthorized to rename a file in this directory",
863                 /* 8c */ "Unauthorized to modify a file in this directory",
864                 /* 8d */ "Some of the affected files are in use by another client",
865                 /* 8e */ "All of the affected files are in use by another client",
866                 /* 8f */ "Some of the affected file are read only",
867                 /* 90 */ "",
868                 /* 91 */ "Some of the affected files already exist",
869                 /* 92 */ "All of the affected files already exist",
870                 /* 93 */ "Unauthorized to read from this file",
871                 /* 94 */ "Unauthorized to write to this file",
872                 /* 95 */ "The affected file is detached",
873                 /* 96 */ "The file server has run out of memory to service this request",
874                 /* 97 */ "Unknown",
875                 /* 98 */ "The affected volume is not mounted",
876                 /* 99 */ "The file server has run out of directory space on the affected volume",
877                 /* 9a */ "The request attempted to rename the affected file to another volume",
878                 /* 9b */ "DirHandle is not associated with a valid directory path",
879                 /* 9c */ "",
880                 /* 9d */ "A directory handle was not available for allocation",
881                 /* 9e */ "The filename does not conform to a legal name for this name space",
882                 /* 9f */ "The request attempted to delete a directory that is in use by another client",
883                 /* a0 */ "The request attempted to delete a directory that is not empty",
884                 /* a1 */ "An unrecoverable error occurred on the affected directory",
885                 /* a2 */ "The request attempted to read from a file region that is physically locked",
886                 /* a3 */ "Unknown",
887                 /* a4 */ "Unknown",
888                 /* a5 */ "Unknown",
889                 /* a6 */ "Unknown",
890                 /* a7 */ "Unknown",
891                 /* a8 */ "Unknown",
892                 /* a9 */ "Unknown",
893                 /* aa */ "Unknown",
894                 /* ab */ "Unknown",
895                 /* ac */ "Unknown",
896                 /* ad */ "Unknown",
897                 /* ae */ "Unknown",
898                 /* af */ "Unknown",
899                 /* b0 */ "Unknown",
900                 /* b1 */ "Unknown",
901                 /* b2 */ "Unknown",
902                 /* b3 */ "Unknown",
903                 /* b4 */ "Unknown",
904                 /* b5 */ "Unknown",
905                 /* b6 */ "Unknown",
906                 /* b7 */ "Unknown",
907                 /* b8 */ "Unknown",
908                 /* b9 */ "Unknown",
909                 /* ba */ "Unknown",
910                 /* bb */ "Unknown",
911                 /* bc */ "Unknown",
912                 /* bd */ "Unknown",
913                 /* be */ "Unknown",
914                 /* bf */ "Requests for this name space are not valid on this volume",
915                 /* c0 */ "Unauthorized to retrieve accounting data",
916                 /* c1 */ "The 'account balance' property does not exist",
917                 /* c2 */ "The object has exceeded its credit limit",
918                 /* c3 */ "Too many holds have been placed against this account",
919                 /* c4 */ "The account for this bindery object has been disabled",
920                 /* c5 */ "Access to the account has been denied because of intruder detections",
921                 /* c6 */ "The caller does not have operator privileges",
922                 /* c7 */ "Unknown",
923                 /* c8 */ "Unknown",
924                 /* c9 */ "Unknown",
925                 /* ca */ "Unknown",
926                 /* cb */ "Unknown",
927                 /* cc */ "Unknown",
928                 /* cd */ "Unknown",
929                 /* ce */ "Unknown",
930                 /* cf */ "Unknown",
931                 /* d0 */ "Queue error",
932                 /* d1 */ "The queue associated with Object ID does not exist",
933                 /* d2 */ "A queue server is not associated with the selected queue",
934                 /* d3 */ "No queue rights",
935                 /* d4 */ "The queue associated with Object ID is full and cannot accept another request",
936                 /* d5 */ "The job associated with Job Number does not exist in this queue",
937                 /* d6 */ "",
938                 /* d7 */ "",
939                 /* d8 */ "Queue not active",
940                 /* d9 */ "",
941                 /* da */ "",
942                 /* db */ "",
943                 /* dc */ "Unknown",
944                 /* dd */ "Unknown",
945                 /* de */ "Attempted to login to the file server with an incorrect password",
946                 /* df */ "Attempted to login to the file server with a password that has expired",
947                 /* e0 */ "Unknown",
948                 /* e1 */ "Unknown",
949                 /* e2 */ "Unknown",
950                 /* e3 */ "Unknown",
951                 /* e4 */ "Unknown",
952                 /* e5 */ "Unknown",
953                 /* e6 */ "Unknown",
954                 /* e7 */ "No disk track",
955                 /* e8 */ "",
956                 /* e9 */ "Unknown",
957                 /* ea */ "The bindery object is not a member of this set",
958                 /* eb */ "The property is not a set property",
959                 /* ec */ "The set property does not exist",
960                 /* ed */ "The property already exists",
961                 /* ee */ "The bindery object already exists",
962                 /* ef */ "Illegal characters in Object Name field",
963                 /* f0 */ "A wildcard was detected in a field that does not support wildcards",
964                 /* f1 */ "The client does not have the rights to access this bindery objecs",
965                 /* f2 */ "Unauthorized to read from this object",
966                 /* f3 */ "Unauthorized to rename this object",
967                 /* f4 */ "Unauthorized to delete this object",
968                 /* f5 */ "Unauthorized to create this object",
969                 /* f6 */ "Unauthorized to delete the property of this object",
970                 /* f7 */ "Unauthorized to create this property",
971                 /* f8 */ "Unauthorized to write to this property",
972                 /* f9 */ "Unauthorized to read this property",
973                 /* fa */ "Temporary remap error",
974                 /* fb */ "",
975                 /* fc */ "",
976                 /* fd */ "",
977                 /* fe */ "",
978                 /* ff */ ""
979         };
980
981         switch (ccode) {
982                 case 0:
983                         return "OK";
984                         break;
985
986                 case 3:
987                         return "Client not accepting messages";
988                         break;
989         }
990
991         if (ccode >= NCP_CCODE_MIN && ccode <= NCP_CCODE_MAX) {
992                 text = ccode_text[ccode - NCP_CCODE_MIN];
993                 /* If there really is text, return it */
994                 if (text[0] != 0)
995                         return text;
996         }
997         else {
998                 return "Unknown";
999         }
1000
1001         /* We have a completion code with multiple translations. We'll use the
1002          * nfamily that this request type belongs to to give the right
1003          * translation.
1004          */
1005         switch (ccode) {
1006
1007                 case 0xfc:
1008                         switch(family) {
1009                                 case NCP_QUEUE_SERVICES:
1010                                         return "The message queue cannot accept another message";
1011                                         break;
1012                                 case NCP_BINDERY_SERVICES:
1013                                         return "The specified bindery object does not exist";
1014                                         break;
1015                                 default:
1016                                         return "Unknown";
1017                                         break;
1018                         }
1019                         break;
1020
1021                 default:
1022                         return "I don't know how to parse this completion code. Please send this packet trace to Gilbert Ramirez <gram@xiexie.org> for analysis";
1023         }
1024 }
1025
1026 void
1027 proto_register_ncp(void)
1028 {
1029
1030   static hf_register_info hf[] = {
1031     { &hf_ncp_ip_sig,
1032       { "NCP over IP signature",                "ncp.ip.signature",
1033         FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
1034         "NCP over IP transport signature"}},
1035     { &hf_ncp_ip_ver,
1036       { "Version",              "ncp.ip.version",
1037         FT_UINT32, BASE_DEC, NULL, 0x0,
1038         "NCP over IP verion"}},
1039     { &hf_ncp_type,
1040       { "Type",                 "ncp.type",
1041         FT_UINT16, BASE_HEX, NULL, 0x0,
1042         "NCP message type" }},
1043     { &hf_ncp_seq,
1044       { "Sequence Number",      "ncp.seq",
1045         FT_UINT8, BASE_DEC, NULL, 0x0,
1046         "" }},
1047     { &hf_ncp_connection,
1048       { "Connection Number",    "ncp.connection",
1049         FT_UINT16, BASE_DEC, NULL, 0x0,
1050         "" }},
1051     { &hf_ncp_task,
1052       { "Task Number",          "ncp.task",
1053         FT_UINT8, BASE_DEC, NULL, 0x0,
1054         "" }}
1055   };
1056   static gint *ett[] = {
1057     &ett_ncp,
1058     &ett_ncp_request_fields,
1059     &ett_ncp_reply_fields,
1060   };
1061
1062   proto_ncp = proto_register_protocol("NetWare Core Protocol", "ncp");
1063   proto_register_field_array(proto_ncp, hf, array_length(hf));
1064   proto_register_subtree_array(ett, array_length(ett));
1065   register_init_routine(&ncp_init_protocol);
1066 }
1067
1068 void
1069 proto_reg_handoff_ncp(void)
1070 {
1071   dissector_add("tcp.port", TCP_PORT_NCP, dissect_ncp);
1072   dissector_add("udp.port", UDP_PORT_NCP, dissect_ncp);
1073 }