Revert "Fixup: tvb_* -> tvb_captured"
[metze/wireshark/wip.git] / epan / dissectors / packet-dsi.c
1 /* packet-dsi.c
2  * Routines for dsi packet dissection
3  * Copyright 2001, Randy McEoin <rmceoin@pe.com>
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * Copied from packet-pop.c
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25
26 #include "config.h"
27
28 #include <glib.h>
29
30 #include <epan/packet.h>
31 #include <epan/prefs.h>
32
33 #include "packet-tcp.h"
34 #include "packet-afp.h"
35
36 /* The information in this module (DSI) comes from:
37
38   AFP 2.1 & 2.2 documentation, in PDF form, at
39
40 http://developer.apple.com/DOCUMENTATION/macos8/pdf/ASAppleTalkFiling2.1_2.2.pdf
41
42   The netatalk source code by Wesley Craig & Adrian Sun
43
44   The Data Stream Interface description from
45   http://developer.apple.com/documentation/Networking/Conceptual/AFPClient/AFPClient-6.html
46
47 (no longer available, apparently)
48
49   Also, AFP 3.3 documents parts of DSI at:
50   http://developer.apple.com/mac/library/documentation/Networking/Conceptual/AFP/Introduction/Introduction.html
51
52  * What a Data Stream Interface packet looks like:
53  * 0                               32
54  * |-------------------------------|
55  * |flags  |command| requestID     |
56  * |-------------------------------|
57  * |error code/enclosed data offset|
58  * |-------------------------------|
59  * |total data length              |
60  * |-------------------------------|
61  * |reserved field                 |
62  * |-------------------------------|
63  */
64
65 void proto_register_dsi(void);
66 void proto_reg_handoff_dsi(void);
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_offset = -1;
73 static int hf_dsi_error = -1;
74 static int hf_dsi_length = -1;
75 static int hf_dsi_reserved = -1;
76
77 static gint ett_dsi = -1;
78
79 static int hf_dsi_open_type     = -1;
80 static int hf_dsi_open_len      = -1;
81 static int hf_dsi_open_quantum  = -1;
82 static int hf_dsi_replay_cache_size = -1;
83 static int hf_dsi_open_option   = -1;
84
85 static int hf_dsi_attn_flag             = -1;
86 static int hf_dsi_attn_flag_shutdown    = -1;
87 static int hf_dsi_attn_flag_crash       = -1;
88 static int hf_dsi_attn_flag_msg         = -1;
89 static int hf_dsi_attn_flag_reconnect   = -1;
90 static int hf_dsi_attn_flag_time        = -1;
91 static int hf_dsi_attn_flag_bitmap      = -1;
92
93 static gint ett_dsi_open        = -1;
94 static gint ett_dsi_attn        = -1;
95 static gint ett_dsi_attn_flag   = -1;
96
97 static const value_string dsi_attn_flag_vals[] = {
98         {0x0, "Reserved" },                                           /* 0000 */
99         {0x1, "Reserved" },                                           /* 0001 */
100         {0x2, "Server message" },                                     /* 0010 */
101         {0x3, "Server notification, cf. extended bitmap" },           /* 0011 */
102         {0x4, "Server is shutting down, internal error" },            /* 0100 */
103         {0x8, "Server is shutting down" },                            /* 1000 */
104         {0x9, "Server disconnects user" },                            /* 1001 */
105         {0x10,"Server is shutting down, message" },                   /* 1010 */
106         {0x11,"Server is shutting down, message,no reconnect"},       /* 1011 */
107         {0,                   NULL } };
108 static value_string_ext dsi_attn_flag_vals_ext = VALUE_STRING_EXT_INIT(dsi_attn_flag_vals);
109
110 static const value_string dsi_open_type_vals[] = {
111         {0,   "Server quantum" },
112         {1,   "Attention quantum" },
113         {2,   "Replay cache size" },
114         {0,                   NULL } };
115
116 /* desegmentation of DSI */
117 static gboolean dsi_desegment = TRUE;
118
119 static dissector_handle_t data_handle;
120 static dissector_handle_t afp_handle;
121 static dissector_handle_t afp_server_status_handle;
122
123 #define TCP_PORT_DSI      548
124
125 #define DSI_BLOCKSIZ       16
126
127 /* DSI flags */
128 #define DSIFL_REQUEST    0x00
129 #define DSIFL_REPLY      0x01
130 #define DSIFL_MAX        0x01
131
132 /* DSI Commands */
133 #define DSIFUNC_CLOSE   1       /* DSICloseSession */
134 #define DSIFUNC_CMD     2       /* DSICommand */
135 #define DSIFUNC_STAT    3       /* DSIGetStatus */
136 #define DSIFUNC_OPEN    4       /* DSIOpenSession */
137 #define DSIFUNC_TICKLE  5       /* DSITickle */
138 #define DSIFUNC_WRITE   6       /* DSIWrite */
139 #define DSIFUNC_ATTN    8       /* DSIAttention */
140 #define DSIFUNC_MAX     8       /* largest command */
141
142 static const value_string flag_vals[] = {
143         {DSIFL_REQUEST,       "Request" },
144         {DSIFL_REPLY,         "Reply" },
145         {0,                   NULL } };
146
147 static const value_string func_vals[] = {
148         {DSIFUNC_CLOSE,       "CloseSession" },
149         {DSIFUNC_CMD,         "Command" },
150         {DSIFUNC_STAT,        "GetStatus" },
151         {DSIFUNC_OPEN,        "OpenSession" },
152         {DSIFUNC_TICKLE,      "Tickle" },
153         {DSIFUNC_WRITE,       "Write" },
154         { 7,                  "Unknown" },
155         {DSIFUNC_ATTN,        "Attention" },
156         {0,                   NULL } };
157 static value_string_ext func_vals_ext = VALUE_STRING_EXT_INIT(func_vals);
158
159 static gint
160 dissect_dsi_open_session(tvbuff_t *tvb, proto_tree *dsi_tree, gint offset, gint dsi_length)
161 {
162         proto_tree      *tree;
163         proto_item      *ti;
164         guint8          type;
165         guint8          len;
166
167         ti = proto_tree_add_text(dsi_tree, tvb, offset, -1, "Open Session");
168         tree = proto_item_add_subtree(ti, ett_dsi_open);
169
170         while( dsi_length >2 ) {
171
172                 type = tvb_get_guint8(tvb, offset);
173                 proto_tree_add_item(tree, hf_dsi_open_type, tvb, offset, 1, ENC_BIG_ENDIAN);
174                 offset++;
175                 len = tvb_get_guint8(tvb, offset);
176                 proto_tree_add_item(tree, hf_dsi_open_len, tvb, offset, 1, ENC_BIG_ENDIAN);
177                 offset++;
178                 switch (type) {
179                         case 0:
180                                 proto_tree_add_item(tree, hf_dsi_open_quantum, tvb, offset, 4, ENC_BIG_ENDIAN);
181                                 break;
182                         case 1:
183                                 proto_tree_add_item(tree, hf_dsi_open_quantum, tvb, offset, 4, ENC_BIG_ENDIAN);
184                                 break;
185                         case 2:
186                                 proto_tree_add_item(tree, hf_dsi_replay_cache_size, tvb, offset, 4, ENC_BIG_ENDIAN);
187                                 break;
188                         default:
189                                 proto_tree_add_item(tree, hf_dsi_open_option, tvb, offset, len, ENC_NA);
190                 }
191
192                 dsi_length -= len + 2;
193
194                 offset += len;
195         }
196         return offset;
197 }
198
199 static gint
200 dissect_dsi_attention(tvbuff_t *tvb, proto_tree *dsi_tree, gint offset)
201 {
202         proto_tree      *tree;
203         proto_item      *ti;
204         guint16         flag;
205
206         if (!tvb_reported_length_remaining(tvb,offset))
207                 return offset;
208
209         flag = tvb_get_ntohs(tvb, offset);
210         ti = proto_tree_add_text(dsi_tree, tvb, offset, -1, "Attention");
211         tree = proto_item_add_subtree(ti, ett_dsi_attn);
212
213         ti = proto_tree_add_item(tree, hf_dsi_attn_flag, tvb, offset, 2, ENC_BIG_ENDIAN);
214         tree = proto_item_add_subtree(ti, ett_dsi_attn_flag);
215         proto_tree_add_item(tree, hf_dsi_attn_flag_shutdown, tvb, offset, 2, ENC_BIG_ENDIAN);
216         proto_tree_add_item(tree, hf_dsi_attn_flag_crash, tvb, offset, 2, ENC_BIG_ENDIAN);
217         proto_tree_add_item(tree, hf_dsi_attn_flag_msg, tvb, offset, 2, ENC_BIG_ENDIAN);
218         proto_tree_add_item(tree, hf_dsi_attn_flag_reconnect, tvb, offset, 2, ENC_BIG_ENDIAN);
219         /* FIXME */
220         if ((flag & 0xf000) != 0x3000)
221                 proto_tree_add_item(tree, hf_dsi_attn_flag_time, tvb, offset, 2, ENC_BIG_ENDIAN);
222         else
223                 proto_tree_add_item(tree, hf_dsi_attn_flag_bitmap, tvb, offset, 2, ENC_BIG_ENDIAN);
224         offset += 2;
225         return offset;
226 }
227
228 static int
229 dissect_dsi_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
230 {
231         proto_tree      *dsi_tree;
232         proto_item      *ti;
233         guint8          dsi_flags,dsi_command;
234         guint16         dsi_requestid;
235         gint32          dsi_code;
236         guint32         dsi_length;
237         guint32         dsi_reserved;
238         struct          aspinfo aspinfo;
239
240
241         col_set_str(pinfo->cinfo, COL_PROTOCOL, "DSI");
242         col_clear(pinfo->cinfo, COL_INFO);
243
244         dsi_flags = tvb_get_guint8(tvb, 0);
245         dsi_command = tvb_get_guint8(tvb, 1);
246         dsi_requestid = tvb_get_ntohs(tvb, 2);
247         dsi_code = tvb_get_ntohl(tvb, 4);
248         dsi_length = tvb_get_ntohl(tvb, 8);
249         dsi_reserved = tvb_get_ntohl(tvb, 12);
250
251         col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s (%u)",
252                         val_to_str(dsi_flags, flag_vals,
253                                    "Unknown flag (0x%02x)"),
254                         val_to_str_ext(dsi_command, &func_vals_ext,
255                                    "Unknown function (0x%02x)"),
256                         dsi_requestid);
257
258         ti = proto_tree_add_item(tree, proto_dsi, tvb, 0, -1, ENC_NA);
259         dsi_tree = proto_item_add_subtree(ti, ett_dsi);
260
261         if (tree) {
262                 proto_tree_add_uint(dsi_tree, hf_dsi_flags, tvb,
263                         0, 1, dsi_flags);
264                 proto_tree_add_uint(dsi_tree, hf_dsi_command, tvb,
265                         1, 1, dsi_command);
266                 proto_tree_add_uint(dsi_tree, hf_dsi_requestid, tvb,
267                         2, 2, dsi_requestid);
268                 switch (dsi_flags) {
269
270                 case DSIFL_REQUEST:
271                         proto_tree_add_int(dsi_tree, hf_dsi_offset, tvb,
272                                 4, 4, dsi_code);
273                         break;
274
275                 case DSIFL_REPLY:
276                         proto_tree_add_int(dsi_tree, hf_dsi_error, tvb,
277                                 4, 4, dsi_code);
278                         break;
279                 }
280                 proto_tree_add_uint_format_value(dsi_tree, hf_dsi_length, tvb,
281                         8, 4, dsi_length,
282                         "%u bytes", dsi_length);
283                 proto_tree_add_uint(dsi_tree, hf_dsi_reserved, tvb,
284                         12, 4, dsi_reserved);
285         }
286
287         switch (dsi_command) {
288         case DSIFUNC_OPEN:
289                 if (tree) {
290                         dissect_dsi_open_session(tvb, dsi_tree, DSI_BLOCKSIZ, dsi_length);
291                 }
292                 break;
293         case DSIFUNC_ATTN:
294                 if (tree) {
295                         dissect_dsi_attention(tvb, dsi_tree, DSI_BLOCKSIZ);
296                 }
297                 break;
298         case DSIFUNC_STAT:
299                 if (tree && (dsi_flags == DSIFL_REPLY)) {
300                         tvbuff_t   *new_tvb;
301
302                         /* XXX - assumes only AFP runs atop DSI */
303                         new_tvb = tvb_new_subset_remaining(tvb, DSI_BLOCKSIZ);
304                         call_dissector(afp_server_status_handle, new_tvb, pinfo, dsi_tree);
305                 }
306                 break;
307         case DSIFUNC_CMD:
308         case DSIFUNC_WRITE:
309                 {
310                         tvbuff_t   *new_tvb;
311
312                         aspinfo.reply = (dsi_flags == DSIFL_REPLY);
313                         aspinfo.command = dsi_command;
314                         aspinfo.seq = dsi_requestid;
315                         aspinfo.code = dsi_code;
316                         proto_item_set_len(dsi_tree, DSI_BLOCKSIZ);
317
318                         new_tvb = tvb_new_subset_remaining(tvb, DSI_BLOCKSIZ);
319                         call_dissector_with_data(afp_handle, new_tvb, pinfo, tree, &aspinfo);
320                 }
321                 break;
322         default:
323                 call_dissector(data_handle,
324                                                 tvb_new_subset_remaining(tvb, DSI_BLOCKSIZ),
325                                                 pinfo, dsi_tree);
326                 break;
327         }
328
329         return tvb_length(tvb);
330 }
331
332 static guint
333 get_dsi_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
334 {
335         guint32 plen;
336         guint8  dsi_flags,dsi_command;
337
338         dsi_flags = tvb_get_guint8(tvb, offset);
339         dsi_command = tvb_get_guint8(tvb, offset+ 1);
340         if ( dsi_flags > DSIFL_MAX || !dsi_command || dsi_command > DSIFUNC_MAX)
341         {
342             /* it's not a known dsi pdu start sequence */
343             return tvb_length_remaining(tvb, offset);
344         }
345
346         /*
347          * Get the length of the DSI packet.
348          */
349         plen = tvb_get_ntohl(tvb, offset+8);
350
351         /*
352          * That length doesn't include the length of the header itself;
353          * add that in.
354          */
355         return plen + 16;
356 }
357
358 static int
359 dissect_dsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
360 {
361         tcp_dissect_pdus(tvb, pinfo, tree, dsi_desegment, 12,
362             get_dsi_pdu_len, dissect_dsi_packet, data);
363
364         return tvb_length(tvb);
365 }
366
367 void
368 proto_register_dsi(void)
369 {
370
371         static hf_register_info hf[] = {
372                 { &hf_dsi_flags,
373                   { "Flags",            "dsi.flags",
374                     FT_UINT8, BASE_HEX, VALS(flag_vals), 0x0,
375                     "Indicates request or reply.", HFILL }},
376
377                 { &hf_dsi_command,
378                   { "Command",          "dsi.command",
379                     FT_UINT8, BASE_DEC|BASE_EXT_STRING, &func_vals_ext, 0x0,
380                     "Represents a DSI command.", HFILL }},
381
382                 { &hf_dsi_requestid,
383                   { "Request ID",       "dsi.requestid",
384                     FT_UINT16, BASE_DEC, NULL, 0x0,
385                     "Keeps track of which request this is.  Replies must match a Request.  IDs must be generated in sequential order.", HFILL }},
386
387                 { &hf_dsi_offset,
388                   { "Data offset",      "dsi.data_offset",
389                     FT_INT32, BASE_DEC, NULL, 0x0,
390                     NULL, HFILL }},
391
392                 { &hf_dsi_error,
393                   { "Error code",       "dsi.error_code",
394                     FT_INT32, BASE_DEC|BASE_EXT_STRING, &asp_error_vals_ext, 0x0,
395                     NULL, HFILL }},
396
397                 { &hf_dsi_length,
398                   { "Length",           "dsi.length",
399                     FT_UINT32, BASE_DEC, NULL, 0x0,
400                     "Total length of the data that follows the DSI header.", HFILL }},
401
402                 { &hf_dsi_reserved,
403                   { "Reserved",         "dsi.reserved",
404                     FT_UINT32, BASE_HEX, NULL, 0x0,
405                     "Reserved for future use.  Should be set to zero.", HFILL }},
406
407                 { &hf_dsi_open_type,
408                   { "Option",          "dsi.open_type",
409                     FT_UINT8, BASE_DEC, VALS(dsi_open_type_vals), 0x0,
410                     "Open session option type.", HFILL }},
411
412                 { &hf_dsi_open_len,
413                   { "Length",          "dsi.open_len",
414                     FT_UINT8, BASE_DEC, NULL, 0x0,
415                     "Open session option len", HFILL }},
416
417                 { &hf_dsi_open_quantum,
418                   { "Quantum",       "dsi.open_quantum",
419                     FT_UINT32, BASE_DEC, NULL, 0x0,
420                     "Server/Attention quantum", HFILL }},
421
422                 { &hf_dsi_replay_cache_size,
423                   { "Replay",       "dsi.replay_cache",
424                     FT_UINT32, BASE_DEC, NULL, 0x0,
425                     "Replay cache size", HFILL }},
426
427                 { &hf_dsi_open_option,
428                   { "Option",          "dsi.open_option",
429                     FT_BYTES, BASE_NONE, NULL, 0x0,
430                     "Open session options (undecoded)", HFILL }},
431
432                 { &hf_dsi_attn_flag,
433                   { "Flags",          "dsi.attn_flag",
434                     FT_UINT16, BASE_HEX|BASE_EXT_STRING, &dsi_attn_flag_vals_ext, 0xf000,
435                     "Server attention flag", HFILL }},
436                 { &hf_dsi_attn_flag_shutdown,
437                   { "Shutdown",      "dsi.attn_flag.shutdown",
438                     FT_BOOLEAN, 16, NULL, 1<<15,
439                     "Attention flag, server is shutting down", HFILL }},
440                 { &hf_dsi_attn_flag_crash,
441                   { "Crash",      "dsi.attn_flag.crash",
442                     FT_BOOLEAN, 16, NULL, 1<<14,
443                     "Attention flag, server crash bit", HFILL }},
444                 { &hf_dsi_attn_flag_msg,
445                   { "Message",      "dsi.attn_flag.msg",
446                     FT_BOOLEAN, 16, NULL, 1<<13,
447                     "Attention flag, server message bit", HFILL }},
448                 { &hf_dsi_attn_flag_reconnect,
449                   { "Don't reconnect",      "dsi.attn_flag.reconnect",
450                     FT_BOOLEAN, 16, NULL, 1<<12,
451                     "Attention flag, don't reconnect bit", HFILL }},
452                 { &hf_dsi_attn_flag_time,
453                   { "Minutes",          "dsi.attn_flag.time",
454                     FT_UINT16, BASE_DEC, NULL, 0xfff,
455                     "Number of minutes", HFILL }},
456                 { &hf_dsi_attn_flag_bitmap,
457                   { "Bitmap",          "dsi.attn_flag.bitmap",
458                     FT_UINT16, BASE_HEX, NULL, 0xfff,
459                     "Attention extended bitmap", HFILL }},
460         };
461
462         static gint *ett[] = {
463                 &ett_dsi,
464                 &ett_dsi_open,
465                 &ett_dsi_attn,
466                 &ett_dsi_attn_flag
467         };
468         module_t *dsi_module;
469
470         proto_dsi = proto_register_protocol("Data Stream Interface", "DSI", "dsi");
471         proto_register_field_array(proto_dsi, hf, array_length(hf));
472         proto_register_subtree_array(ett, array_length(ett));
473
474         dsi_module = prefs_register_protocol(proto_dsi, NULL);
475         prefs_register_bool_preference(dsi_module, "desegment",
476                                        "Reassemble DSI messages spanning multiple TCP segments",
477                                        "Whether the DSI dissector should reassemble messages spanning multiple TCP segments."
478                                        " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
479                                        &dsi_desegment);
480 }
481
482 void
483 proto_reg_handoff_dsi(void)
484 {
485         dissector_handle_t dsi_handle;
486
487         dsi_handle = new_create_dissector_handle(dissect_dsi, proto_dsi);
488         dissector_add_uint("tcp.port", TCP_PORT_DSI, dsi_handle);
489
490         data_handle = find_dissector("data");
491         afp_handle = find_dissector("afp");
492         afp_server_status_handle = find_dissector("afp_server_status");
493 }