Hopefully the last time I have to change my e-mail address.
[obnox/wireshark/wip.git] / packet-ncp.c
1 /* packet-ncp.c
2  * Routines for NetWare Core Protocol
3  * Gilbert Ramirez <gram@alumni.rice.edu>
4  * Modified to allow NCP over TCP/IP decodes by James Coe <jammer@cin.net>
5  *
6  * $Id: packet-ncp.c,v 1.50 2001/11/13 23:55:30 gram Exp $
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@zing.org>
10  * Copyright 2000 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 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #ifdef HAVE_SYS_TYPES_H
33 # include <sys/types.h>
34 #endif
35
36 #ifdef HAVE_NETINET_IN_H
37 # include <netinet/in.h>
38 #endif
39
40 #include <glib.h>
41 #include "packet.h"
42 #include "conversation.h"
43 #include "prefs.h"
44 #include "packet-ipx.h"
45 #include "packet-ncp-int.h"
46
47 int proto_ncp = -1;
48 static int hf_ncp_ip_ver = -1;
49 static int hf_ncp_ip_length = -1;
50 static int hf_ncp_ip_rplybufsize = -1;
51 static int hf_ncp_ip_sig = -1;
52 static int hf_ncp_type = -1;
53 static int hf_ncp_seq = -1;
54 static int hf_ncp_connection = -1;
55 static int hf_ncp_task = -1;
56
57 static gint ett_ncp = -1;
58
59 #define TCP_PORT_NCP            524
60 #define UDP_PORT_NCP            524
61
62 #define NCP_RQST_HDR_LENGTH     7
63 #define NCP_RPLY_HDR_LENGTH     8
64
65 /* Hash functions */
66 gint  ncp_equal (gconstpointer v, gconstpointer v2);
67 guint ncp_hash  (gconstpointer v);
68
69 static guint ncp_packet_init_count = 200;
70
71 /* These are the header structures to handle NCP over IP */
72 #define NCPIP_RQST      0x446d6454      /* "DmdT" */
73 #define NCPIP_RPLY      0x744e6350      /* "tNcP" */
74
75 struct ncp_ip_header {
76         guint32 signature;
77         guint32 length;
78 };
79
80 /* This header only appears on NCP over IP request packets */
81 struct ncp_ip_rqhdr {
82         guint32 version;
83         guint32 rplybufsize;
84 };
85
86 static const value_string ncp_ip_signature[] = {
87         { NCPIP_RQST, "Demand Transport (Request)" },
88         { NCPIP_RPLY, "Transport is NCP (Reply)" },
89         { 0, NULL },
90 };
91
92 /* The information in this module comes from:
93         NetWare LAN Analysis, Second Edition
94         Laura A. Chappell and Dan E. Hakes
95         (c) 1994 Novell, Inc.
96         Novell Press, San Jose.
97         ISBN: 0-7821-1362-1
98
99   And from the ncpfs source code by Volker Lendecke
100
101   And:
102         Programmer's Guide to the NetWare Core Protocol
103         Steve Conner & Diane Conner
104         (c) 1996 by Steve Conner & Diane Conner
105         Published by Annabooks, San Diego, California
106         ISBN: 0-929392-31-0
107
108 */
109
110 /* Every NCP packet has this common header */
111 struct ncp_common_header {
112         guint16 type;
113         guint8  sequence;
114         guint8  conn_low;
115         guint8  task;
116         guint8  conn_high; /* type=0x5555 doesn't have this */
117 };
118
119
120 static value_string ncp_type_vals[] = {
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
131 /* NCP packets come in request/reply pairs. The request packets tell the type
132  * of NCP request and give a sequence ID. The response, unfortunately, only
133  * identifies itself via the sequence ID; you have to know what type of NCP
134  * request the request packet contained in order to successfully parse the NCP
135  * response. A global method for doing this does not exist in ethereal yet
136  * (NFS also requires it), so for now the NCP section will keep its own hash
137  * table keeping track of NCP packet types.
138  *
139  * We construct a conversation specified by the client and server
140  * addresses and the connection number; the key representing the unique
141  * NCP request then is composed of the pointer to the conversation
142  * structure, cast to a "guint" (which may throw away the upper 32
143  * bits of the pointer on a P64 platform, but the low-order 32 bits
144  * are more likely to differ between conversations than the upper 32 bits),
145  * and the sequence number.
146  *
147  * The value stored in the hash table is the ncp_request_val pointer. This
148  * struct tells us the NCP type and gives the ncp2222_record pointer, if
149  * ncp_type == 0x2222.
150  */
151 typedef struct {
152         conversation_t  *conversation;
153         guint8          nw_sequence;
154 } ncp_request_key;
155
156 typedef struct {
157         guint16                 ncp_type;
158         const ncp_record        *ncp_record;
159 } ncp_request_val;
160
161
162 static GHashTable *ncp_request_hash = NULL;
163 static GMemChunk *ncp_request_keys = NULL;
164 static GMemChunk *ncp_request_records = NULL;
165
166 /* Hash Functions */
167 gint  ncp_equal (gconstpointer v, gconstpointer v2)
168 {
169         ncp_request_key *val1 = (ncp_request_key*)v;
170         ncp_request_key *val2 = (ncp_request_key*)v2;
171
172         if (val1->conversation == val2->conversation &&
173             val1->nw_sequence  == val2->nw_sequence ) {
174                 return 1;
175         }
176         return 0;
177 }
178
179 guint ncp_hash  (gconstpointer v)
180 {
181         ncp_request_key *ncp_key = (ncp_request_key*)v;
182         return GPOINTER_TO_UINT(ncp_key->conversation) + ncp_key->nw_sequence;
183 }
184
185 /* Initializes the hash table and the mem_chunk area each time a new
186  * file is loaded or re-loaded in ethereal */
187 static void
188 ncp_init_protocol(void)
189 {
190         if (ncp_request_hash)
191                 g_hash_table_destroy(ncp_request_hash);
192         if (ncp_request_keys)
193                 g_mem_chunk_destroy(ncp_request_keys);
194         if (ncp_request_records)
195                 g_mem_chunk_destroy(ncp_request_records);
196
197         ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal);
198         ncp_request_keys = g_mem_chunk_new("ncp_request_keys",
199                         sizeof(ncp_request_key),
200                         ncp_packet_init_count * sizeof(ncp_request_key), G_ALLOC_AND_FREE);
201         ncp_request_records = g_mem_chunk_new("ncp_request_records",
202                         sizeof(ncp_request_val),
203                         ncp_packet_init_count * sizeof(ncp_request_val), G_ALLOC_AND_FREE);
204 }
205
206 void
207 ncp_hash_insert(conversation_t *conversation, guint8 nw_sequence,
208                 guint16 ncp_type, const ncp_record *ncp_rec)
209 {
210         ncp_request_val         *request_val;
211         ncp_request_key         *request_key;
212
213         /* Now remember the request, so we can find it if we later
214            a reply to it. */
215         request_key = g_mem_chunk_alloc(ncp_request_keys);
216         request_key->conversation = conversation;
217         request_key->nw_sequence = nw_sequence;
218
219         request_val = g_mem_chunk_alloc(ncp_request_records);
220         request_val->ncp_type = ncp_type;
221         request_val->ncp_record = ncp_rec;
222
223         g_hash_table_insert(ncp_request_hash, request_key, request_val);
224 }
225
226 /* Returns TRUE or FALSE. If TRUE, the record was found and
227  * ncp_type and ncp_rec are set. */
228 gboolean
229 ncp_hash_lookup(conversation_t *conversation, guint8 nw_sequence,
230                 guint16 *ncp_type, const ncp_record **ncp_rec)
231 {
232         ncp_request_val         *request_val;
233         ncp_request_key         request_key;
234
235         request_key.conversation = conversation;
236         request_key.nw_sequence = nw_sequence;
237
238         request_val = (ncp_request_val*)
239                 g_hash_table_lookup(ncp_request_hash, &request_key);
240
241         if (request_val) {
242                 *ncp_type       = request_val->ncp_type;
243                 *ncp_rec        = request_val->ncp_record;
244                 return TRUE;
245         }
246         else {
247                 return FALSE;
248         }
249 }
250
251 static void
252 dissect_ncp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
253 {
254         proto_tree                      *ncp_tree = NULL;
255         proto_item                      *ti;
256         struct ncp_ip_header            ncpiph;
257         struct ncp_ip_rqhdr             ncpiphrq;
258         struct ncp_common_header        header;
259         guint16                         nw_connection;
260         int                             hdr_offset = 0;
261         int                             commhdr;
262         tvbuff_t                        *next_tvb;
263
264         if (check_col(pinfo->fd, COL_PROTOCOL))
265                 col_set_str(pinfo->fd, COL_PROTOCOL, "NCP");
266         if (check_col(pinfo->fd, COL_INFO))
267                 col_clear(pinfo->fd, COL_INFO);
268
269         if ( pinfo->ptype == PT_TCP || pinfo->ptype == PT_UDP ) {
270                 ncpiph.signature        = tvb_get_ntohl(tvb, 0);
271                 ncpiph.length           = tvb_get_ntohl(tvb, 4);
272                 hdr_offset += 8;
273                 if ( ncpiph.signature == NCPIP_RQST ) {
274                         ncpiphrq.version        = tvb_get_ntohl(tvb, hdr_offset);
275                         hdr_offset += 4;
276                         ncpiphrq.rplybufsize    = tvb_get_ntohl(tvb, hdr_offset);
277                         hdr_offset += 4;
278                 };
279         };
280
281         /* Record the offset where the NCP common header starts */
282         commhdr = hdr_offset;
283
284         header.type             = tvb_get_ntohs(tvb, commhdr);
285         header.sequence         = tvb_get_guint8(tvb, commhdr+2);
286         header.conn_low         = tvb_get_guint8(tvb, commhdr+3);
287         header.conn_high        = tvb_get_guint8(tvb, commhdr+5);
288
289         nw_connection = (header.conn_high << 16) + header.conn_low;
290
291         if (tree) {
292                 ti = proto_tree_add_item(tree, proto_ncp, tvb, 0, tvb_length(tvb), FALSE);
293                 ncp_tree = proto_item_add_subtree(ti, ett_ncp);
294
295                 if ( pinfo->ptype == PT_TCP || pinfo->ptype == PT_UDP ) {
296                         proto_tree_add_uint(ncp_tree, hf_ncp_ip_sig, tvb, 0, 4, ncpiph.signature);
297                         proto_tree_add_uint(ncp_tree, hf_ncp_ip_length, tvb, 4, 4, ncpiph.length);
298                         if ( ncpiph.signature == NCPIP_RQST ) {
299                                 proto_tree_add_uint(ncp_tree, hf_ncp_ip_ver, tvb, 8, 4, ncpiphrq.version);
300                                 proto_tree_add_uint(ncp_tree, hf_ncp_ip_rplybufsize, tvb, 12, 4, ncpiphrq.rplybufsize);
301                         };
302                 };
303                 proto_tree_add_uint(ncp_tree, hf_ncp_type,      tvb, commhdr + 0, 2, header.type);
304                 proto_tree_add_uint(ncp_tree, hf_ncp_seq,       tvb, commhdr + 2, 1, header.sequence);
305                 proto_tree_add_uint(ncp_tree, hf_ncp_connection,tvb, commhdr + 3, 3, nw_connection);
306                 proto_tree_add_item(ncp_tree, hf_ncp_task,      tvb, commhdr + 4, 1, FALSE);
307         }
308
309
310         if (header.type == 0x1111 || header.type == 0x2222) {
311                 next_tvb = tvb_new_subset( tvb, hdr_offset, -1, -1 );
312                 dissect_ncp_request(next_tvb, pinfo, nw_connection,
313                         header.sequence, header.type, ncp_tree, tree);
314         }
315         else if (header.type == 0x3333) {
316                 next_tvb = tvb_new_subset( tvb, hdr_offset, -1, -1 );
317                 dissect_ncp_reply(next_tvb, pinfo, nw_connection,
318                         header.sequence, ncp_tree, tree);
319         }
320         else if (       header.type == 0x5555 ||
321                         header.type == 0x7777 ||
322                         header.type == 0x9999           ) {
323
324                 if (check_col(pinfo->fd, COL_INFO)) {
325                         col_add_fstr(pinfo->fd, COL_INFO, "Type 0x%04x", header.type);
326                 }
327
328                 if (tree) {
329                         proto_tree_add_text(ncp_tree, tvb, commhdr + 0, 2, "Type 0x%04x not supported yet", header.type);
330                 }
331
332                 return;
333         }
334         else {
335                 /* The value_string for hf_ncp_type already indicates that this type is unknown.
336                  * Just return and do no more parsing. */
337                 return;
338         }
339 }
340
341
342
343 void
344 proto_register_ncp(void)
345 {
346
347   static hf_register_info hf[] = {
348     { &hf_ncp_ip_sig,
349       { "NCP over IP signature",                "ncp.ip.signature",
350         FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
351         "", HFILL }},
352     { &hf_ncp_ip_length,
353       { "NCP over IP length",           "ncp.ip.length",
354         FT_UINT32, BASE_HEX, NULL, 0x0,
355         "", HFILL }},
356     { &hf_ncp_ip_ver,
357       { "NCP over IP Version",          "ncp.ip.version",
358         FT_UINT32, BASE_DEC, NULL, 0x0,
359         "", HFILL }},
360     { &hf_ncp_ip_rplybufsize,
361       { "NCP over IP Reply Buffer Size",        "ncp.ip.replybufsize",
362         FT_UINT32, BASE_DEC, NULL, 0x0,
363         "", HFILL }},
364     { &hf_ncp_type,
365       { "Type",                 "ncp.type",
366         FT_UINT16, BASE_HEX, VALS(ncp_type_vals), 0x0,
367         "NCP message type", HFILL }},
368     { &hf_ncp_seq,
369       { "Sequence Number",      "ncp.seq",
370         FT_UINT8, BASE_DEC, NULL, 0x0,
371         "", HFILL }},
372     { &hf_ncp_connection,
373       { "Connection Number",    "ncp.connection",
374         FT_UINT16, BASE_DEC, NULL, 0x0,
375         "", HFILL }},
376     { &hf_ncp_task,
377       { "Task Number",          "ncp.task",
378         FT_UINT8, BASE_DEC, NULL, 0x0,
379         "", HFILL }}
380   };
381   static gint *ett[] = {
382     &ett_ncp,
383   };
384   module_t *ncp_module;
385
386   proto_ncp = proto_register_protocol("NetWare Core Protocol", "NCP", "ncp");
387   proto_register_field_array(proto_ncp, hf, array_length(hf));
388   proto_register_subtree_array(ett, array_length(ett));
389   register_init_routine(&ncp_init_protocol);
390
391   /* Register a configuration option for initial size of NCP hash */
392   ncp_module = prefs_register_protocol(proto_ncp, NULL);
393   prefs_register_uint_preference(ncp_module, "initial_hash_size",
394         "Initial Hash Size",
395         "Number of entries initially allocated for NCP hash",
396         10, &ncp_packet_init_count);
397 }
398
399 void
400 proto_reg_handoff_ncp(void)
401 {
402   dissector_add("tcp.port", TCP_PORT_NCP, dissect_ncp, proto_ncp);
403   dissector_add("udp.port", UDP_PORT_NCP, dissect_ncp, proto_ncp);
404   dissector_add("ipx.packet_type", IPX_PACKET_TYPE_NCP, dissect_ncp,
405                 proto_ncp);
406   dissector_add("ipx.socket", IPX_SOCKET_NCP, dissect_ncp, proto_ncp);
407 }