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