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