Fixed some serious bugs in the NCP hash routines. I also simplified
[metze/wireshark/wip.git] / packet-ncp.c
1 /* packet-ncp.c
2  * Routines for NetWare Core Protocol
3  * Gilbert Ramirez <gram@verdict.uthscsa.edu>
4  *
5  * $Id: packet-ncp.c,v 1.12 1999/05/13 16:42:43 gram Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@unicom.net>
9  * Copyright 1998 Gerald Combs
10  *
11  * 
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #undef DEBUG_NCP_HASH
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
35 #endif
36
37 #ifdef HAVE_NETINET_IN_H
38 # include <netinet/in.h>
39 #endif
40
41 #include <glib.h>
42 #include "packet.h"
43 #include "packet-ipx.h"
44 #include "packet-ncp.h"
45
46 struct svc_record;
47
48 static void
49 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
50
51 static void
52 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
53
54 static struct ncp2222_record *
55 ncp2222_find(guint8 func, guint8 subfunc);
56
57 static void
58 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
59         struct svc_record *svc);
60
61 static int
62 svc_record_byte_count(struct svc_record *sr);
63
64 /* Hash functions */
65 gint  ncp_equal (const gpointer v, const gpointer v2);
66 guint ncp_hash  (const gpointer v);
67
68
69 /* The information in this module comes from:
70         NetWare LAN Analysis, Second Edition
71         Laura A. Chappell and Dan E. Hakes
72         (c) 1994 Novell, Inc.
73         Novell Press, San Jose.
74         ISBN: 0-7821-1362-1
75
76   And from the ncpfs source code by Volker Lendecke
77
78   And:
79         Programmer's Guide to the NetWare Core Protocol
80         Steve Conner & Diane Conner
81         (c) 1996 by Steve Conner & Diane Conner
82         Published by Annabooks, San Diego, California
83         ISBN: 0-929392-31-0
84
85 */
86
87 /* Every NCP packet has this common header */
88 struct ncp_common_header {
89         guint16 type;
90         guint8  sequence;
91         guint8  conn_low;
92         guint8  task;
93         guint8  conn_high;
94 };
95
96 /* NCP request packets */
97 struct ncp_request_header {
98         guint16 type;
99         guint8  sequence;
100         guint8  conn_low;
101         guint8  task;
102         guint8  conn_high;
103         guint8  function;
104         guint16 length;
105         guint8  subfunc;
106 };
107
108 /* NCP reply packets */
109 struct ncp_reply_header {
110         guint16 type;
111         guint8  sequence;
112         guint8  conn_low;
113         guint8  task;
114         guint8  conn_high;
115         guint8  completion_code;
116         guint8  connection_state;
117 };
118
119
120 static value_string request_reply_values[] = {
121         { 0x1111, "Create a service connection" },
122         { 0x2222, "Service request" },
123         { 0x3333, "Service reply" },
124         { 0x5555, "Destroy service connection" },
125         { 0x7777, "Burst mode transfer" },
126         { 0x9999, "Request being processed" },
127         { 0x0000, NULL }
128 };
129
130 /* These are the field types in an NCP packet */
131 enum ntype {
132         nend,           /* end of the NCP field list */
133         nbyte,          /* one byte of data */
134         nhex,
135         nbelong,        /* 4-byte big-endian long int */
136         nbeshort,       /* 2-byte big-endian short int */
137         ndata,          /* unstructured data */
138         nbytevar,       /* a variable number of bytes */
139         ndatetime,      /* date-time stamp */
140         nasciiz         /* null-terminated string of ASCII characters */
141 };
142
143 /* Information on the NCP field */
144 typedef struct svc_record {
145         enum ntype      type;
146         guint8          length;
147         gchar           *description;
148 } svc_record;
149
150 typedef struct ncp2222_record {
151         guint8          func;
152         guint8          subfunc;
153         guint8          submask;
154         gchar           *funcname;
155
156         svc_record      *req;
157         svc_record      *rep;
158         void*           special_handler;
159
160 } ncp2222_record;
161
162 /* Service Queue Job REQUEST */
163 static svc_record ncp_17_7C_C[] = {
164                 { nbelong,      4,      "The queue the job resides in" },
165                 { nbeshort,     2,      "Job Type" },
166                 { nend,         0,      NULL }
167 };
168 /* Service Queue Job REPLY */
169 static svc_record ncp_17_7C_R[] = {
170                 { nbelong,      4,      "Client station number" },
171                 { nbelong,      4,      "Task Number" },
172                 { nbelong,      4,      "User" },
173                 { nbelong,      4,      "Server specifed to service queue entry" },
174                 { ndatetime,    6,      "Earliest time to execute" },
175                 { ndatetime,    6,      "When job entered queue" },
176                 { nbelong,      4,      "Job Number" },
177                 { nbeshort,     2,      "Job Type" },
178                 { nbeshort,     2,      "Job Position" },
179                 { nbeshort,     2,      "Current status of job" },
180                 { nasciiz,      14,     "Name of file" },
181                 { nbelong,      4,      "File handle" },
182                 { nbelong,      4,      "Client station number" },
183                 { nbelong,      4,      "Task number" },
184                 { nbelong,      4,      "Job server" },
185                 { nend,         0,      NULL }
186 };
187
188 /* Negotiate Buffer Size REQUEST */
189 static svc_record ncp_21_00_C[] = {
190                 { nbeshort,     2,      "Caller's maximum packet size: %d bytes" },
191                 { nend,         0,      NULL }
192 };
193 /* RESPONSE */
194 static svc_record ncp_21_00_R[] = {
195                 { nbeshort,     2,      "Packet size decided upon by file server: %d" },
196                 { nend,         0,      NULL }
197 };
198
199 /* Read from a file REQUEST */
200 static svc_record ncp_48_00_C[] = {
201                 { nbyte,        1,      "Unknown" },
202                 { nhex,         6,      "File Handle" },
203                 { nbelong,      4,      "Byte offset within file" },
204                 { nbeshort,     2,      "Maximum data bytes to return" },
205                 { nend,         0,      NULL }
206 };
207 /* RESPONSE */
208 static svc_record ncp_48_00_R[] = {
209                 { nbeshort,     2,      "Data bytes returned" },
210                 { nbytevar,     1,      "Padding" },
211                 { ndata,        0,      NULL }
212 };
213
214 #define SUBFUNC 0xff
215 #define NOSUB   0x00
216
217 static ncp2222_record ncp2222[] = {
218
219 { 0x17, 0x7C, SUBFUNC, "Service Queue Job",
220         ncp_17_7C_C, ncp_17_7C_R, NULL
221 },
222
223 { 0x21, 0x00, NOSUB, "Negotiate Buffer Size",
224         ncp_21_00_C, ncp_21_00_R, NULL
225 },
226
227 { 0x48, 0x00, NOSUB, "Read from a file",
228         ncp_48_00_C, ncp_48_00_R, NULL
229 },
230
231 { 0x00, 0x00, NOSUB, NULL,
232         NULL, NULL, NULL
233 }
234
235 };
236
237 /* NCP packets come in request/reply pairs. The request packets tell the type
238  * of NCP request and give a sequence ID. The response, unfortunately, only
239  * identifies itself via the sequence ID; you have to know what type of NCP
240  * request the request packet contained in order to successfully parse the NCP
241  * response. A global method for doing this does not exist in ethereal yet
242  * (NFS also requires it), so for now the NCP section will keep its own hash
243  * table keeping track of NCP packet types.
244  *
245  * The key representing the unique NCP request is composed of 3 variables:
246  *
247  * ServerIPXNetwork.Connection.SequenceNumber
248  *     4 bytes        2 bytes      1 byte
249  *     guint32        guint16      guint8     (all are host order)
250  *
251  * This assumes that all NCP connection is between a client and server.
252  * Servers can be identified by having a 00:00:00:00:00:01 IPX Node address.
253  * We have to let the IPX layer pass us the ServerIPXNetwork via a global
254  * variable (nw_server_address). In the future, if we decode NCP over TCP/UDP,
255  * then nw_server_address will represent the IP address of the server, which
256  * conveniently, is also 4 bytes long.
257  *
258  * The value stored in the hash table is the ncp_request_val pointer. This
259  * struct tells us the NCP type and gives the ncp2222_record pointer, if
260  * ncp_type == 0x2222.
261  */
262 guint32 nw_server_address = 0; /* set by IPX layer */
263 guint16 nw_connection = 0; /* set by dissect_ncp */
264 guint8  nw_sequence = 0; /* set by dissect_ncp */
265 guint16 nw_ncp_type = 0; /* set by dissect_ncp */
266
267 struct ncp_request_key {
268         guint32 nw_server_address;
269         guint16 nw_connection;
270         guint8  nw_sequence;
271 };
272
273 struct ncp_request_val {
274         guint32                                 ncp_type;
275         struct ncp2222_record*  ncp_record;
276 };
277
278 GHashTable *ncp_request_hash = NULL;
279 GMemChunk *ncp_request_keys = NULL;
280 GMemChunk *ncp_request_records = NULL;
281
282 /* Hash Functions */
283 gint  ncp_equal (const gpointer v, const gpointer v2)
284 {
285         struct ncp_request_key  *val1 = (struct ncp_request_key*)v;
286         struct ncp_request_key  *val2 = (struct ncp_request_key*)v2;
287
288         #if defined(DEBUG_NCP_HASH)
289         printf("Comparing %08X:%d:%d and %08X:%d:%d\n",
290                 val1->nw_server_address, val1->nw_connection, val1->nw_sequence,
291                 val2->nw_server_address, val2->nw_connection, val2->nw_sequence);
292         #endif
293
294         if (val1->nw_server_address == val2->nw_server_address &&
295                 val1->nw_connection == val2->nw_connection &&
296                 val1->nw_sequence   == val2->nw_sequence ) {
297                 return 1;
298         }
299         return 0;
300 }
301
302 guint ncp_hash  (const gpointer v)
303 {
304         struct ncp_request_key  *ncp_key = (struct ncp_request_key*)v;
305 #if defined(DEBUG_NCP_HASH)
306         printf("hash calculated as %d\n", ncp_key->nw_server_address +
307                         ((guint32) ncp_key->nw_connection << 16) +
308                         ncp_key->nw_sequence);
309 #endif
310         return ncp_key->nw_server_address +
311                         ((guint32) ncp_key->nw_connection << 16) +
312                         ncp_key->nw_sequence;
313 }
314
315 /* Initializes the hash table and the mem_chunk area each time a new
316  * file is loaded or re-loaded in ethereal */
317 void
318 ncp_init_protocol(void)
319 {
320         #if defined(DEBUG_NCP_HASH)
321         printf("Initializing NCP hashtable and mem_chunk area\n");
322         #endif
323         if (ncp_request_hash)
324                 g_hash_table_destroy(ncp_request_hash);
325         if (ncp_request_keys)
326                 g_mem_chunk_destroy(ncp_request_keys);
327         if (ncp_request_records)
328                 g_mem_chunk_destroy(ncp_request_records);
329
330         ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal);
331         ncp_request_keys = g_mem_chunk_new("ncp_request_keys",
332                         sizeof(struct ncp_request_key),
333                         100 * sizeof(struct ncp_request_key), G_ALLOC_AND_FREE);
334         ncp_request_records = g_mem_chunk_new("ncp_request_records",
335                         sizeof(struct ncp_request_val),
336                         100 * sizeof(struct ncp_request_val), G_ALLOC_AND_FREE);
337 }
338
339 static struct ncp2222_record *
340 ncp2222_find(guint8 func, guint8 subfunc)
341 {
342         struct ncp2222_record *ncp_record, *retval = NULL;
343
344         ncp_record = ncp2222;
345
346         while(ncp_record->func != 0) {
347                 if (ncp_record->func == func &&
348                         ncp_record->subfunc == (subfunc & ncp_record->submask)) {
349                         retval = ncp_record;
350                         break;
351                 }
352                 ncp_record++;
353         }
354
355         return retval;
356 }
357
358 /* How many bytes of NCP data to expect in the packet */
359 static int
360 svc_record_byte_count(svc_record *sr)
361 {
362         svc_record *rec = sr;
363         int byte_count = 0;
364
365         while (rec->type != nend && rec->type != ndata) {
366                 byte_count += rec->length;
367                 rec++;
368         }
369
370         return byte_count;
371 }
372
373 void
374 dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
375         int max_data) {
376
377         proto_tree      *ncp_tree = NULL;
378         proto_item      *ti;
379         int             ncp_hdr_length = 0;
380         struct ncp_common_header        header;
381
382         memcpy(&header, &pd[offset], sizeof(header));
383         header.type = ntohs(header.type);
384
385         if (header.type == 0x1111 ||
386                         header.type == 0x2222 ||
387                         header.type == 0x5555 ||
388                         header.type == 0x7777) {
389                 ncp_hdr_length = 7;
390         }
391         else if (header.type == 0x3333 || header.type == 0x9999) {
392                 ncp_hdr_length = 8;
393         }
394
395         if (check_col(fd, COL_PROTOCOL))
396                 col_add_str(fd, COL_PROTOCOL, "NCP");
397
398         nw_connection = (header.conn_high << 16) + header.conn_low;
399         nw_sequence = header.sequence;
400         nw_ncp_type = header.type;
401
402         if (tree) {
403                 ti = proto_tree_add_item(tree, offset, END_OF_FRAME,
404                         "NetWare Core Protocol");
405                 ncp_tree = proto_tree_new();
406                 proto_item_add_subtree(ti, ncp_tree, ETT_NCP);
407
408                 proto_tree_add_item(ncp_tree, offset,      2,
409                         "Type: %s", val_to_str( header.type,
410                         request_reply_values, "Unknown (%04X)"));
411
412                 proto_tree_add_item(ncp_tree, offset+2,    1,
413                         "Sequence Number: %d", header.sequence);
414
415                 proto_tree_add_item(ncp_tree, offset+3,    3,
416                         "Connection Number: %d", nw_connection);
417
418                 proto_tree_add_item(ncp_tree, offset+4,    1,
419                         "Task Number: %d", header.task);
420         }
421
422         /* Note how I use ncp_tree *and* tree in my args for ncp request/reply */
423         if (ncp_hdr_length == 7)
424                 dissect_ncp_request(pd, offset, fd, ncp_tree, tree);
425         else if (ncp_hdr_length == 8)
426                 dissect_ncp_reply(pd, offset, fd, ncp_tree, tree);
427         else
428                 dissect_data(pd, offset, fd, tree);
429 }
430
431 void
432 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
433         proto_tree *ncp_tree, proto_tree *tree) {
434
435         struct ncp_request_header       request;
436         struct ncp2222_record           *ncp_request;
437         gchar                           *description = "";
438         struct ncp_request_val          *request_val;
439         struct ncp_request_key          *request_key;
440         proto_tree                      *field_tree = NULL;
441         proto_item                      *ti = NULL;
442         int                             max_data;
443
444         /*memcpy(&request, &pd[offset], sizeof(request));*/
445         request.function = pd[offset+6];
446         request.subfunc = pd[offset+9];
447
448         ncp_request = ncp2222_find(request.function, request.subfunc);
449
450         if (ncp_request)
451                 description = ncp_request->funcname;
452
453         if (check_col(fd, COL_INFO)) {
454                 if (description[0]) {
455                         col_add_fstr(fd, COL_INFO, "C %s", description);
456                 }
457                 else {
458                         col_add_fstr(fd, COL_INFO, "C 2222/%02X%02X",
459                                 request.function, request.subfunc);
460                 }
461         }
462
463         if (ncp_tree) {
464                 proto_tree_add_item(ncp_tree, offset+6, 1,
465                         "Function Code: 0x%02X (%s)",
466                         request.function, description);
467
468                 if (ncp_request) {
469                         max_data = svc_record_byte_count(ncp_request->req);
470
471                         if (max_data > 0) {
472                                 ti = proto_tree_add_item(ncp_tree, offset+7, END_OF_FRAME,
473                                 "NCP Request Packet");
474                                 field_tree = proto_tree_new();
475                                 proto_item_add_subtree(ti, field_tree, ETT_NCP_REQUEST_FIELDS);
476
477                                 parse_ncp_svc_fields(pd, field_tree, offset+7, ncp_request->req);
478                         }
479                 }
480         }
481         else { /* ! tree */
482                 request_key = g_mem_chunk_alloc(ncp_request_keys);
483                 request_key->nw_server_address = nw_server_address;
484                 request_key->nw_connection = nw_connection;
485                 request_key->nw_sequence = nw_sequence;
486
487                 request_val = g_mem_chunk_alloc(ncp_request_records);
488                 request_val->ncp_type = nw_ncp_type;
489                 request_val->ncp_record = ncp2222_find(request.function, request.subfunc);
490
491                 g_hash_table_insert(ncp_request_hash, request_key, request_val);
492                 #if defined(DEBUG_NCP_HASH)
493                 printf("Inserted server %08X connection %d sequence %d (val=%08X)\n",
494                         nw_server_address, nw_connection, nw_sequence, request_val);
495                 #endif
496         }
497
498 }
499
500 void
501 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
502         proto_tree *ncp_tree, proto_tree *tree) {
503
504         struct ncp_reply_header         reply;
505         struct ncp2222_record           *ncp_request = NULL;
506         struct ncp_request_val          *request_val;
507         struct ncp_request_key          request_key;
508         gchar                           *description = "Unknown";
509         proto_tree                      *field_tree = NULL;
510         proto_item                      *ti = NULL;
511         int                             max_data;
512
513         memcpy(&reply, &pd[offset], sizeof(reply));
514
515         /* find the record telling us the request made that caused this reply */
516         request_key.nw_server_address = nw_server_address;
517         request_key.nw_connection = nw_connection;
518         request_key.nw_sequence = nw_sequence;
519
520         request_val = (struct ncp_request_val*)
521                 g_hash_table_lookup(ncp_request_hash, &request_key);
522
523         #if defined(DEBUG_NCP_HASH)
524         printf("Looking for server %08X connection %d sequence %d (retval=%08X)\n",
525                 nw_server_address, nw_connection, nw_sequence, request_val);
526         #endif
527
528         if (request_val)
529                 ncp_request = request_val->ncp_record;
530
531         if (ncp_request)
532                 description = ncp_request->funcname;
533
534         if (check_col(fd, COL_INFO))
535                 col_add_fstr(fd, COL_INFO, "R %s", description);
536
537         if (ncp_tree) {
538                 proto_tree_add_item(ncp_tree, offset+6,    1,
539                         "Completion Code: %d", reply.completion_code);
540
541                 proto_tree_add_item(ncp_tree, offset+7,    1,
542                         "Connection Status: %d", reply.connection_state);
543
544                 if (ncp_request) {
545                         max_data = svc_record_byte_count(ncp_request->req);
546
547                         if (max_data > 0) {
548                                 ti = proto_tree_add_item(ncp_tree, offset+8, END_OF_FRAME,
549                                 "NCP Reply Packet");
550                                 field_tree = proto_tree_new();
551                                 proto_item_add_subtree(ti, field_tree, ETT_NCP_REPLY_FIELDS);
552
553                                 parse_ncp_svc_fields(pd, field_tree, offset+8, ncp_request->req);
554                         }
555                 }
556         }
557
558 }
559
560 /* Populates the protocol tree with information about the svc_record fields */
561 static void
562 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
563         struct svc_record *svc)
564 {
565         struct svc_record *rec = svc;
566         int field_offset = offset;
567
568         while (rec->type != nend) {
569                 switch(rec->type) {
570                         case nbeshort:
571                                 proto_tree_add_item(ncp_tree, field_offset,
572                                         rec->length, rec->description, pntohs(&pd[field_offset]));
573                                 break;
574
575                         /* default:
576                                 nothing */
577                 }
578                 field_offset += rec->length;
579                 rec++;
580         }       
581 }