38c99bc04564445400b76e4be96fb409dd5c917a
[obnox/wireshark/wip.git] / packet-dsi.c
1 /* packet-dsi.c
2  * Routines for dsi packet dissection
3  * Copyright 2001, Randy McEoin <rmceoin@pe.com>
4  *
5  * $Id: packet-dsi.c,v 1.7 2001/12/10 00:25:27 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
11  * Copied from packet-pop.c
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 #include <stdio.h>
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 <string.h>
43 #include <glib.h>
44 #include "packet.h"
45 #include "strutil.h"
46 #include "conversation.h"
47
48 /* The information in this module (DSI) comes from:
49
50   AFP 2.1 & 2.2.pdf contained in AppleShare_IP_6.3_SDK
51   available from http://www.apple.com
52
53   The netatalk source code by Wesley Craig & Adrian Sun
54
55  * What a Data Stream Interface packet looks like:
56  * 0                               32
57  * |-------------------------------|
58  * |flags  |command| requestID     |
59  * |-------------------------------|
60  * |error code/enclosed data offset|
61  * |-------------------------------|
62  * |total data length              |
63  * |-------------------------------|
64  * |reserved field                 |
65  * |-------------------------------|
66 */
67
68 static int proto_dsi = -1;
69 static int hf_dsi_flags = -1;
70 static int hf_dsi_command = -1;
71 static int hf_dsi_requestid = -1;
72 static int hf_dsi_code = -1;
73 static int hf_dsi_length = -1;
74 static int hf_dsi_reserved = -1;
75
76 static gint ett_dsi = -1;
77
78 static dissector_handle_t dsi_handle;
79 static dissector_handle_t data_handle;
80
81 #define TCP_PORT_DSI                    548
82
83 /* DSI flags */
84 #define DSIFL_REQUEST    0x00
85 #define DSIFL_REPLY      0x01
86 #define DSIFL_MAX        0x01
87
88 /* DSI Commands */
89 #define DSIFUNC_CLOSE   1       /* DSICloseSession */
90 #define DSIFUNC_CMD     2       /* DSICommand */
91 #define DSIFUNC_STAT    3       /* DSIGetStatus */
92 #define DSIFUNC_OPEN    4       /* DSIOpenSession */
93 #define DSIFUNC_TICKLE  5       /* DSITickle */
94 #define DSIFUNC_WRITE   6       /* DSIWrite */
95 #define DSIFUNC_ATTN    8       /* DSIAttention */
96 #define DSIFUNC_MAX     8       /* largest command */
97
98 static const value_string flag_vals[] = {
99   {DSIFL_REQUEST,       "Request" },
100   {DSIFL_REPLY,         "Reply" },
101   {0,                   NULL } };
102
103 static const value_string func_vals[] = {
104   {DSIFUNC_CLOSE,       "CloseSession" },
105   {DSIFUNC_CMD,         "Command" },
106   {DSIFUNC_STAT,        "GetStatus" },
107   {DSIFUNC_OPEN,        "OpenSession" },
108   {DSIFUNC_TICKLE,      "Tickle" },
109   {DSIFUNC_WRITE,       "Write" },
110   {DSIFUNC_ATTN,        "Attention" },
111   {0,                   NULL } };
112
113
114 static GMemChunk *vals = NULL;
115
116 typedef struct {
117         int     state;
118         guint8  flags,command;
119         guint16 requestid;
120         guint32 code;
121         guint32 length;         /* total length of this DSI request/reply */
122         guint32 reserved;
123         guint32 seen;           /* bytes seen so far */
124 }hash_entry_t;
125
126 enum {NONE,FIRSTDATA,MOREDATA,DONE};
127
128 #define hash_init_count 20
129 #define hash_val_length (sizeof(hash_entry_t))
130
131 static guint32 last_abs_sec = 0;
132 static guint32 last_abs_usec= 0;
133 static guint32 highest_num = 0;
134
135 /* Hash functions */
136 gint  dsi_equal (gconstpointer v, gconstpointer v2);
137 guint dsi_hash  (gconstpointer v);
138  
139 static guint dsi_packet_init_count = 200;
140
141 typedef struct {
142         guint32 packetnum;
143 } dsi_request_key;
144  
145 typedef struct {
146         guint8  flags;
147         guint8  command;
148         guint16 requestid;
149         guint32 length;
150         guint32 seen;           /* bytes seen so far, including this packet */
151 } dsi_request_val;
152  
153 static GHashTable *dsi_request_hash = NULL;
154 static GMemChunk *dsi_request_keys = NULL;
155 static GMemChunk *dsi_request_records = NULL;
156
157 /* Hash Functions */
158 gint  dsi_equal (gconstpointer v, gconstpointer v2)
159 {
160         dsi_request_key *val1 = (dsi_request_key*)v;
161         dsi_request_key *val2 = (dsi_request_key*)v2;
162
163         if (val1->packetnum == val2->packetnum) {
164                 return 1;
165         }
166         return 0;
167 }
168
169 guint dsi_hash  (gconstpointer v)
170 {
171         dsi_request_key *dsi_key = (dsi_request_key*)v;
172         return GPOINTER_TO_UINT(dsi_key->packetnum);
173 }
174
175 void
176 dsi_hash_insert(guint32 packetnum, guint8 flags, guint8 command,
177         guint16 requestid, guint32 length, guint32 seen)
178 {
179         dsi_request_val         *request_val;
180         dsi_request_key         *request_key;
181
182         /* Now remember info about this continuation packet */
183
184         request_key = g_mem_chunk_alloc(dsi_request_keys);
185         request_key->packetnum = packetnum;
186
187         request_val = g_mem_chunk_alloc(dsi_request_records);
188         request_val->flags = flags;
189         request_val->command = command;
190         request_val->requestid = requestid;
191         request_val->length = length;
192         request_val->seen = seen;
193
194         g_hash_table_insert(dsi_request_hash, request_key, request_val);
195 }
196
197 /* Returns TRUE or FALSE. If TRUE, the record was found */
198
199 gboolean
200 dsi_hash_lookup(guint32 packetnum, guint8 *flags, guint8 *command,
201                 guint16 *requestid, guint32 *length, guint32 *seen)
202 {
203         dsi_request_val         *request_val;
204         dsi_request_key         request_key;
205
206         request_key.packetnum = packetnum;
207
208         request_val = (dsi_request_val*)
209                 g_hash_table_lookup(dsi_request_hash, &request_key);
210
211         if (request_val) {
212                 *flags          = request_val->flags;
213                 *command        = request_val->command;
214                 *requestid      = request_val->requestid;
215                 *length         = request_val->length;
216                 *seen           = request_val->seen;
217                 return TRUE;
218         }
219         else {
220                 return FALSE;
221         }
222 }
223
224 /* The state_machine remembers information about continuation packets */
225 /* returns TRUE if it found a previously known continuation packet */
226 gboolean
227 dsi_state_machine( hash_entry_t *hash_info, tvbuff_t *tvb, packet_info *pinfo,
228         int offset)
229 {
230         frame_data *fd;
231         guint32 data_here;
232         guint8  flags,command;
233         guint16 requestid;
234         guint32 length;
235         guint32 seen;
236         gboolean found_hash;
237
238         fd=pinfo->fd;
239
240         found_hash=dsi_hash_lookup(fd->num, &flags, &command, &requestid,
241                 &length, &seen);
242         if (found_hash==TRUE)
243         {
244                 hash_info->flags = flags;
245                 hash_info->command = command;
246                 hash_info->requestid = requestid;
247                 hash_info->length = length;
248                 hash_info->seen = seen;
249                 return TRUE;
250         }
251
252         /* is this sequentially the next packet? */
253         if (highest_num > fd->num)
254         {
255                 hash_info->state = NONE;
256                 return FALSE;
257         }
258
259         highest_num = fd->num;
260
261         if ((hash_info->state == NONE) || (hash_info->state == DONE))
262         {
263                 hash_info->state = NONE;
264                 hash_info->length = tvb_get_ntohl(tvb, offset+8);
265                 data_here = tvb_length_remaining(tvb, offset+16);
266                 if (data_here < hash_info->length)
267                 {
268                         hash_info->flags = tvb_get_guint8(tvb, offset);
269                         hash_info->command = tvb_get_guint8(tvb, offset+1);
270                         hash_info->requestid = tvb_get_ntohs(tvb, offset+2);
271                         hash_info->code = tvb_get_ntohl(tvb, offset+4);
272                         hash_info->reserved = tvb_get_ntohl(tvb, offset+12);
273                         hash_info->seen = data_here;
274                         hash_info->state = FIRSTDATA;
275                 }
276                 return FALSE;
277         }
278
279
280         if (hash_info->state == FIRSTDATA)
281                 hash_info->state = MOREDATA;
282
283         /* we must be receiving more data */
284         data_here = tvb_length_remaining(tvb, offset);
285         hash_info->seen += data_here;
286         if (hash_info->seen >= hash_info->length)
287                 hash_info->state = DONE;
288
289         dsi_hash_insert(fd->num, hash_info->flags,
290                 hash_info->command, hash_info->requestid,
291                 hash_info->length,hash_info->seen);
292
293         return FALSE;
294 }
295
296 static void
297 dissect_dsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
298 {
299         proto_tree      *dsi_tree;
300         proto_item      *ti;
301         conversation_t  *conversation;
302         hash_entry_t    *hash_info;
303         gint            offset = 0;
304         gboolean        prev_cont;      /* TRUE if a previously known
305                                         * continuation packet */
306         char            cont_str[256];
307
308         gchar   *flag_str;
309         gchar   *func_str;
310         guint8  dsi_flags,dsi_command;
311         guint16 dsi_requestid;
312         guint32 dsi_code;
313         guint32 dsi_length;
314         guint32 dsi_reserved;
315  
316         if (check_col(pinfo->cinfo, COL_PROTOCOL))
317                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DSI");
318         if (check_col(pinfo->cinfo, COL_INFO))
319                 col_clear(pinfo->cinfo, COL_INFO);
320
321         conversation = find_conversation(&pinfo->src, &pinfo->dst, PT_TCP,
322                 pinfo->srcport, pinfo->destport, 0);
323         if (conversation == NULL)
324         {
325                 conversation = conversation_new(&pinfo->src, &pinfo->dst,
326                         pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
327         }
328         conversation_set_dissector(conversation, dsi_handle);
329         hash_info = conversation_get_proto_data(conversation, proto_dsi);
330         if (hash_info == NULL)
331         {
332                 hash_info = g_mem_chunk_alloc(vals);
333                 hash_info->state = NONE;
334                 conversation_add_proto_data(conversation, proto_dsi,
335                         hash_info);
336         }
337
338         prev_cont=dsi_state_machine( hash_info, tvb, pinfo, offset);
339
340         if ((hash_info->state == NONE) && (prev_cont!=TRUE))
341         {
342                 dsi_flags = tvb_get_guint8(tvb, offset);
343                 dsi_command = tvb_get_guint8(tvb, offset+1);
344                 dsi_requestid = tvb_get_ntohs(tvb, offset+2);
345                 dsi_code = tvb_get_ntohl(tvb, offset+4);
346                 dsi_length = tvb_get_ntohl(tvb, offset+8);
347                 dsi_reserved = tvb_get_ntohl(tvb, offset+12);
348         }else
349         {
350                 dsi_flags = hash_info->flags;
351                 dsi_command = hash_info->command;
352                 dsi_requestid = hash_info->requestid;
353                 dsi_code = hash_info->code;
354                 dsi_length = hash_info->length;
355                 dsi_reserved = hash_info->reserved;
356         }
357
358         if (check_col(pinfo->cinfo, COL_INFO)) {
359                 if ((func_str = match_strval(dsi_command, func_vals)))
360                 {
361                         flag_str = match_strval(dsi_flags, flag_vals);
362                         if ((hash_info->state == MOREDATA) ||
363                                 (hash_info->state == DONE) ||
364                                 (prev_cont == TRUE))
365                         {
366                                 sprintf(cont_str,"Continued: %d/%d",
367                                         hash_info->seen,hash_info->length);
368                         }else
369                         {
370                                 cont_str[0]=0;
371                         }
372                         col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s (%d) %s",
373                                 flag_str,func_str,dsi_requestid,
374                                 cont_str);
375                 }
376         }
377
378
379         if (tree)
380         {
381                 ti = proto_tree_add_item(tree, proto_dsi, tvb, offset,
382                     tvb_length_remaining(tvb, offset), FALSE);
383                 dsi_tree = proto_item_add_subtree(ti, ett_dsi);
384
385                 if (prev_cont == TRUE)
386                 {
387                         proto_tree_add_uint(dsi_tree, hf_dsi_requestid, tvb,
388                                 0, 0, dsi_requestid);
389                         call_dissector(data_handle,tvb, pinfo, dsi_tree);
390                 }else
391                 {
392                         proto_tree_add_uint(dsi_tree, hf_dsi_flags, tvb,
393                                 offset, 1, dsi_flags);
394                         proto_tree_add_uint(dsi_tree, hf_dsi_command, tvb,
395                                 offset+1, 1, dsi_command);
396                         proto_tree_add_uint(dsi_tree, hf_dsi_requestid, tvb,
397                                 offset+2, 2, dsi_requestid);
398                         proto_tree_add_uint(dsi_tree, hf_dsi_code, tvb,
399                                 offset+4, 4, dsi_code);
400                         proto_tree_add_uint_format(dsi_tree, hf_dsi_length, tvb,
401                                 offset+8, 4, dsi_length,
402                                 "Length: %d bytes", dsi_length);
403                         proto_tree_add_uint(dsi_tree, hf_dsi_reserved, tvb,
404                                 offset+12, 4, dsi_reserved);
405                         call_dissector(data_handle,tvb_new_subset(tvb, 16,-1,tvb_reported_length_remaining(tvb,16)), pinfo, dsi_tree);
406                 }
407
408         }
409 }
410
411 static void dsi_reinit( void){                                              
412
413         last_abs_sec = 0;
414         last_abs_usec= 0;
415         highest_num = 0;
416
417         if (vals)
418                 g_mem_chunk_destroy(vals);
419         if (dsi_request_hash)
420                 g_hash_table_destroy(dsi_request_hash);
421         if (dsi_request_keys)
422                 g_mem_chunk_destroy(dsi_request_keys);
423         if (dsi_request_records)
424                 g_mem_chunk_destroy(dsi_request_records);
425
426         dsi_request_hash = g_hash_table_new(dsi_hash, dsi_equal);
427
428         dsi_request_keys = g_mem_chunk_new("dsi_request_keys",
429                 sizeof(dsi_request_key),
430                 dsi_packet_init_count * sizeof(dsi_request_key),
431                 G_ALLOC_AND_FREE);
432         dsi_request_records = g_mem_chunk_new("dsi_request_records",
433                 sizeof(dsi_request_val),
434                 dsi_packet_init_count * sizeof(dsi_request_val),
435                 G_ALLOC_AND_FREE);
436
437         vals = g_mem_chunk_new("dsi_vals", hash_val_length,
438                 hash_init_count * hash_val_length,
439                 G_ALLOC_AND_FREE);
440 }
441
442 void
443 proto_register_dsi(void)
444 {
445
446   static hf_register_info hf[] = {
447     { &hf_dsi_flags,
448       { "Flags",            "dsi.flags",
449         FT_UINT8, BASE_HEX, VALS(flag_vals), 0x0,
450         "Indicates request or reply.", HFILL }},
451
452     { &hf_dsi_command,
453       { "Command",           "dsi.command",
454         FT_UINT8, BASE_DEC, VALS(func_vals), 0x0,
455         "Represents a DSI command.", HFILL }},
456
457     { &hf_dsi_requestid,
458       { "Request ID",           "dsi.requestid",
459         FT_UINT16, BASE_DEC, NULL, 0x0,
460         "Keeps track of which request this is.  Replies must match a Request.  IDs must be generated in sequential order.", HFILL }},
461
462     { &hf_dsi_code,
463       { "Code",           "dsi.code",
464         FT_UINT32, BASE_HEX, NULL, 0x0,
465         "In Reply packets this is an error code.  In Request Write packets this is a data offset.", HFILL }},
466
467     { &hf_dsi_length,
468       { "Length",           "dsi.length",
469         FT_UINT32, BASE_DEC, NULL, 0x0,
470         "Total length of the data that follows the DSI header.", HFILL }},
471
472     { &hf_dsi_reserved,
473       { "Reserved",           "dsi.reserved",
474         FT_UINT32, BASE_HEX, NULL, 0x0,
475         "Reserved for future use.  Should be set to zero.", HFILL }},
476
477   };
478   static gint *ett[] = {
479     &ett_dsi,
480   };
481
482   proto_dsi = proto_register_protocol("Data Stream Interface", "DSI", "dsi");
483   proto_register_field_array(proto_dsi, hf, array_length(hf));
484   proto_register_subtree_array(ett, array_length(ett));
485
486   register_init_routine( &dsi_reinit);
487
488   dsi_handle = create_dissector_handle(dissect_dsi, proto_dsi);
489 }
490
491 void
492 proto_reg_handoff_dsi(void)
493 {
494   data_handle = find_dissector("data");
495   dissector_add("tcp.port", TCP_PORT_DSI, dsi_handle);
496 }