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