ATP, ASP, and AFP support, from Didier Gautheron.
[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.12 2002/04/25 23:58:02 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 <glib.h>
43 #include <epan/packet.h>
44
45 #include "prefs.h"
46 #include "packet-frame.h"
47
48 #include "packet-afp.h"
49
50 /* The information in this module (DSI) comes from:
51
52   AFP 2.1 & 2.2.pdf contained in AppleShare_IP_6.3_SDK
53   available from http://www.apple.com
54
55   The netatalk source code by Wesley Craig & Adrian Sun
56
57  * What a Data Stream Interface packet looks like:
58  * 0                               32
59  * |-------------------------------|
60  * |flags  |command| requestID     |
61  * |-------------------------------|
62  * |error code/enclosed data offset|
63  * |-------------------------------|
64  * |total data length              |
65  * |-------------------------------|
66  * |reserved field                 |
67  * |-------------------------------|
68  */
69
70 static int proto_dsi = -1;
71 static int hf_dsi_flags = -1;
72 static int hf_dsi_command = -1;
73 static int hf_dsi_requestid = -1;
74 static int hf_dsi_code = -1;
75 static int hf_dsi_length = -1;
76 static int hf_dsi_reserved = -1;
77
78 static gint ett_dsi = -1;
79
80 /* desegmentation of DSI */
81 static gboolean dsi_desegment = TRUE;
82
83 static dissector_handle_t data_handle;
84 static dissector_handle_t afp_handle;
85
86 #define TCP_PORT_DSI                    548
87
88 #define DSI_BLOCKSIZ            16
89
90 /* DSI flags */
91 #define DSIFL_REQUEST    0x00
92 #define DSIFL_REPLY      0x01
93 #define DSIFL_MAX        0x01
94
95 /* DSI Commands */
96 #define DSIFUNC_CLOSE   1       /* DSICloseSession */
97 #define DSIFUNC_CMD     2       /* DSICommand */
98 #define DSIFUNC_STAT    3       /* DSIGetStatus */
99 #define DSIFUNC_OPEN    4       /* DSIOpenSession */
100 #define DSIFUNC_TICKLE  5       /* DSITickle */
101 #define DSIFUNC_WRITE   6       /* DSIWrite */
102 #define DSIFUNC_ATTN    8       /* DSIAttention */
103 #define DSIFUNC_MAX     8       /* largest command */
104
105 static const value_string flag_vals[] = {
106   {DSIFL_REQUEST,       "Request" },
107   {DSIFL_REPLY,         "Reply" },
108   {0,                   NULL } };
109
110 static const value_string func_vals[] = {
111   {DSIFUNC_CLOSE,       "CloseSession" },
112   {DSIFUNC_CMD,         "Command" },
113   {DSIFUNC_STAT,        "GetStatus" },
114   {DSIFUNC_OPEN,        "OpenSession" },
115   {DSIFUNC_TICKLE,      "Tickle" },
116   {DSIFUNC_WRITE,       "Write" },
117   {DSIFUNC_ATTN,        "Attention" },
118   {0,                   NULL } };
119
120 static void
121 dissect_dsi_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
122 {
123         proto_tree      *dsi_tree;
124         proto_item      *ti;
125         guint8          dsi_flags,dsi_command;
126         guint16         dsi_requestid;
127         gint32          dsi_code;
128         guint32         dsi_length;
129         guint32         dsi_reserved;
130         struct          aspinfo aspinfo;
131  
132         if (check_col(pinfo->cinfo, COL_PROTOCOL))
133                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DSI");
134         if (check_col(pinfo->cinfo, COL_INFO))
135                 col_clear(pinfo->cinfo, COL_INFO);
136
137         dsi_flags = tvb_get_guint8(tvb, 0);
138         dsi_command = tvb_get_guint8(tvb, 1);
139         dsi_requestid = tvb_get_ntohs(tvb, 2);
140         dsi_code = tvb_get_ntohl(tvb, 4);
141         dsi_length = tvb_get_ntohl(tvb, 8);
142         dsi_reserved = tvb_get_ntohl(tvb, 12);
143
144         if (check_col(pinfo->cinfo, COL_INFO)) {
145                 col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s (%u)",
146                         val_to_str(dsi_flags, flag_vals,
147                                    "Unknown flag (0x%02x)"),
148                         val_to_str(dsi_command, func_vals,
149                                    "Unknown function (0x%02x)"),
150                         dsi_requestid);
151         }
152
153
154         if (tree) {
155                 ti = proto_tree_add_item(tree, proto_dsi, tvb, 0, -1, FALSE);
156                 dsi_tree = proto_item_add_subtree(ti, ett_dsi);
157
158                 proto_tree_add_uint(dsi_tree, hf_dsi_flags, tvb,
159                         0, 1, dsi_flags);
160                 proto_tree_add_uint(dsi_tree, hf_dsi_command, tvb,
161                         1, 1, dsi_command);
162                 proto_tree_add_uint(dsi_tree, hf_dsi_requestid, tvb,
163                         2, 2, dsi_requestid);
164                 proto_tree_add_int(dsi_tree, hf_dsi_code, tvb,
165                         4, 4, dsi_code);
166                 proto_tree_add_uint_format(dsi_tree, hf_dsi_length, tvb,
167                         8, 4, dsi_length,
168                         "Length: %u bytes", dsi_length);
169                 proto_tree_add_uint(dsi_tree, hf_dsi_reserved, tvb,
170                         12, 4, dsi_reserved);
171         }
172         else 
173                 dsi_tree = tree;
174
175         if (dsi_command == DSIFUNC_CMD || dsi_command == DSIFUNC_WRITE) {
176                 tvbuff_t   *new_tvb;
177                 int len = tvb_reported_length_remaining(tvb,DSI_BLOCKSIZ);
178
179                 if (len) {
180                         aspinfo.reply = dsi_flags & 1;
181                         aspinfo.command = dsi_command;
182                         aspinfo.seq = dsi_requestid;
183                         aspinfo.code = dsi_code;
184                         pinfo->private_data = &aspinfo;
185                         proto_item_set_len(dsi_tree, DSI_BLOCKSIZ);
186
187                         new_tvb = tvb_new_subset(tvb, DSI_BLOCKSIZ,-1,len);
188                         call_dissector(afp_handle, new_tvb, pinfo, tree);
189                 }
190         }
191         else if (tree) {        
192                 call_dissector(data_handle,tvb_new_subset(tvb, DSI_BLOCKSIZ,-1,tvb_reported_length_remaining(tvb,DSI_BLOCKSIZ)), pinfo, dsi_tree); 
193         }
194 }
195
196 static void
197 dissect_dsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
198 {
199         volatile int offset = 0;
200         int length_remaining;
201         guint32 plen;
202         int length;
203         tvbuff_t *next_tvb;
204
205         while (tvb_reported_length_remaining(tvb, offset) != 0) {
206                 length_remaining = tvb_length_remaining(tvb, offset);
207
208                 /*
209                  * Can we do reassembly?
210                  */
211                 if (dsi_desegment && pinfo->can_desegment) {
212                         /*
213                          * Yes - is the DSI header split across segment
214                          * boundaries?
215                          */
216                         if (length_remaining < 12) {
217                                 /*
218                                  * Yes.  Tell the TCP dissector where
219                                  * the data for this message starts in
220                                  * the data it handed us, and how many
221                                  * more bytes we need, and return.
222                                  */
223                                 pinfo->desegment_offset = offset;
224                                 pinfo->desegment_len = 12 - length_remaining;
225                                 return;
226                         }
227                 }
228
229                 /*
230                  * Get the length of the DSI packet.
231                  */
232                 plen = tvb_get_ntohl(tvb, offset+8);
233
234                 /*
235                  * Can we do reassembly?
236                  */
237                 if (dsi_desegment && pinfo->can_desegment) {
238                         /*
239                          * Yes - is the DSI packet split across segment
240                          * boundaries?
241                          */
242                         if ((guint32)length_remaining < plen + 16) {
243                                 /*
244                                  * Yes.  Tell the TCP dissector where
245                                  * the data for this message starts in
246                                  * the data it handed us, and how many
247                                  * more bytes we need, and return.
248                                  */
249                                 pinfo->desegment_offset = offset;
250                                 pinfo->desegment_len =
251                                     (plen + 16) - length_remaining;
252                                 return;
253                         }
254                 }
255
256                 /*
257                  * Construct a tvbuff containing the amount of the payload
258                  * we have available.  Make its reported length the
259                  * amount of data in the DSI packet.
260                  *
261                  * XXX - if reassembly isn't enabled. the subdissector
262                  * will throw a BoundsError exception, rather than a
263                  * ReportedBoundsError exception.  We really want
264                  * a tvbuff where the length is "length", the reported
265                  * length is "plen + 16", and the "if the snapshot length
266                  * were infinite" length is the minimum of the
267                  * reported length of the tvbuff handed to us and "plen+16",
268                  * with a new type of exception thrown if the offset is
269                  * within the reported length but beyond that third length,
270                  * with that exception getting the "Unreassembled Packet"
271                  * error.
272                  */
273                 length = length_remaining;
274                 if ((guint32)length > plen + 16)
275                         length = plen + 16;
276                 next_tvb = tvb_new_subset(tvb, offset, length, plen + 16);
277
278                 /*
279                  * Dissect the DSI packet.
280                  *
281                  * Catch the ReportedBoundsError exception; if this
282                  * particular message happens to get a ReportedBoundsError
283                  * exception, that doesn't mean that we should stop
284                  * dissecting DSI messages within this frame or chunk
285                  * of reassembled data.
286                  *
287                  * If it gets a BoundsError, we can stop, as there's nothing
288                  * more to see, so we just re-throw it.
289                  */
290                 TRY {
291                         dissect_dsi_packet(next_tvb, pinfo, tree);
292                 }
293                 CATCH(BoundsError) {
294                         RETHROW;
295                 }
296                 CATCH(ReportedBoundsError) {
297                         show_reported_bounds_error(tvb, pinfo, tree);
298                 }
299                 ENDTRY;
300
301                 /*
302                  * Skip the DSI header and the payload.
303                  */
304                 offset += plen + 16;
305         }
306 }
307
308 void
309 proto_register_dsi(void)
310 {
311
312   static hf_register_info hf[] = {
313     { &hf_dsi_flags,
314       { "Flags",            "dsi.flags",
315         FT_UINT8, BASE_HEX, VALS(flag_vals), 0x0,
316         "Indicates request or reply.", HFILL }},
317
318     { &hf_dsi_command,
319       { "Command",           "dsi.command",
320         FT_UINT8, BASE_DEC, VALS(func_vals), 0x0,
321         "Represents a DSI command.", HFILL }},
322
323     { &hf_dsi_requestid,
324       { "Request ID",           "dsi.requestid",
325         FT_UINT16, BASE_DEC, NULL, 0x0,
326         "Keeps track of which request this is.  Replies must match a Request.  IDs must be generated in sequential order.", HFILL }},
327
328     { &hf_dsi_code,
329       { "Code",           "dsi.code",
330         FT_INT32, BASE_DEC, NULL, 0x0,
331         "In Reply packets this is an error code.  In Request Write packets this is a data offset.", HFILL }},
332
333     { &hf_dsi_length,
334       { "Length",           "dsi.length",
335         FT_UINT32, BASE_DEC, NULL, 0x0,
336         "Total length of the data that follows the DSI header.", HFILL }},
337
338     { &hf_dsi_reserved,
339       { "Reserved",           "dsi.reserved",
340         FT_UINT32, BASE_HEX, NULL, 0x0,
341         "Reserved for future use.  Should be set to zero.", HFILL }},
342
343   };
344   static gint *ett[] = {
345     &ett_dsi,
346   };
347   module_t *dsi_module;
348
349   proto_dsi = proto_register_protocol("Data Stream Interface", "DSI", "dsi");
350   proto_register_field_array(proto_dsi, hf, array_length(hf));
351   proto_register_subtree_array(ett, array_length(ett));
352
353   dsi_module = prefs_register_protocol(proto_dsi, NULL);
354   prefs_register_bool_preference(dsi_module, "desegment",
355     "Desegment all DSI messages spanning multiple TCP segments",
356     "Whether the DSI dissector should desegment all messages spanning multiple TCP segments",
357     &dsi_desegment);
358 }
359
360 void
361 proto_reg_handoff_dsi(void)
362 {
363   static dissector_handle_t dsi_handle;
364
365   dsi_handle = create_dissector_handle(dissect_dsi, proto_dsi);
366   dissector_add("tcp.port", TCP_PORT_DSI, dsi_handle);
367
368   data_handle = find_dissector("data");
369   afp_handle = find_dissector("afp");
370 }