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