2 * Routines for dsi packet dissection
3 * Copyright 2001, Randy McEoin <rmceoin@pe.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * Copied from packet-pop.c
11 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include <epan/packet.h>
17 #include <epan/prefs.h>
18 #include <epan/unit_strings.h>
20 #include "packet-tcp.h"
21 #include "packet-afp.h"
23 /* The information in this module (DSI) comes from:
25 AFP 2.1 & 2.2 documentation, in PDF form, at
27 http://developer.apple.com/DOCUMENTATION/macos8/pdf/ASAppleTalkFiling2.1_2.2.pdf
29 which is no longer available and does not appear to be in the Wayback Machine.
31 The netatalk source code by Wesley Craig & Adrian Sun
33 The Data Stream Interface description from
35 http://developer.apple.com/documentation/Networking/Conceptual/AFPClient/AFPClient-6.html
37 which is no longer available, but is archived at
39 https://web.archive.org/web/20040924082047/http://developer.apple.com/documentation/Networking/Conceptual/AFPClient/AFPClient-6.html
41 Also, AFP 3.3 documents parts of DSI at:
43 http://developer.apple.com/mac/library/documentation/Networking/Conceptual/AFP/Introduction/Introduction.html
45 * What a Data Stream Interface packet looks like:
47 * |-------------------------------|
48 * |flags |command| requestID |
49 * |-------------------------------|
50 * |error code/enclosed data offset|
51 * |-------------------------------|
52 * |total data length |
53 * |-------------------------------|
55 * |-------------------------------|
58 void proto_register_dsi(void);
59 void proto_reg_handoff_dsi(void);
61 static dissector_handle_t dsi_handle
;
64 static int hf_dsi_flags
;
65 static int hf_dsi_command
;
66 static int hf_dsi_requestid
;
67 static int hf_dsi_offset
;
68 static int hf_dsi_error
;
69 static int hf_dsi_length
;
70 static int hf_dsi_reserved
;
74 static int hf_dsi_open_type
;
75 static int hf_dsi_open_len
;
76 static int hf_dsi_open_quantum
;
77 static int hf_dsi_replay_cache_size
;
78 static int hf_dsi_open_option
;
80 static int hf_dsi_attn_flag
;
81 static int hf_dsi_attn_flag_shutdown
;
82 static int hf_dsi_attn_flag_crash
;
83 static int hf_dsi_attn_flag_msg
;
84 static int hf_dsi_attn_flag_reconnect
;
85 static int hf_dsi_attn_flag_time
;
86 static int hf_dsi_attn_flag_bitmap
;
88 static int ett_dsi_open
;
89 static int ett_dsi_attn
;
90 static int ett_dsi_attn_flag
;
92 static const value_string dsi_attn_flag_vals
[] = {
93 {0x0, "Reserved" }, /* 0000 */
94 {0x1, "Reserved" }, /* 0001 */
95 {0x2, "Server message" }, /* 0010 */
96 {0x3, "Server notification, cf. extended bitmap" }, /* 0011 */
97 {0x4, "Server is shutting down, internal error" }, /* 0100 */
98 {0x8, "Server is shutting down" }, /* 1000 */
99 {0x9, "Server disconnects user" }, /* 1001 */
100 {0x10,"Server is shutting down, message" }, /* 1010 */
101 {0x11,"Server is shutting down, message,no reconnect"}, /* 1011 */
103 static value_string_ext dsi_attn_flag_vals_ext
= VALUE_STRING_EXT_INIT(dsi_attn_flag_vals
);
105 static const value_string dsi_open_type_vals
[] = {
106 {0, "Server quantum" },
107 {1, "Attention quantum" },
108 {2, "Replay cache size" },
111 /* desegmentation of DSI */
112 static bool dsi_desegment
= true;
114 static dissector_handle_t afp_handle
;
115 static dissector_handle_t afp_server_status_handle
;
117 #define TCP_PORT_DSI 548 /* Not IANA registered */
119 #define DSI_BLOCKSIZ 16
122 #define DSIFL_REQUEST 0x00
123 #define DSIFL_REPLY 0x01
124 #define DSIFL_MAX 0x01
127 #define DSIFUNC_CLOSE 1 /* DSICloseSession */
128 #define DSIFUNC_CMD 2 /* DSICommand */
129 #define DSIFUNC_STAT 3 /* DSIGetStatus */
130 #define DSIFUNC_OPEN 4 /* DSIOpenSession */
131 #define DSIFUNC_TICKLE 5 /* DSITickle */
132 #define DSIFUNC_WRITE 6 /* DSIWrite */
133 #define DSIFUNC_ATTN 8 /* DSIAttention */
134 #define DSIFUNC_MAX 8 /* largest command */
136 static const value_string flag_vals
[] = {
137 {DSIFL_REQUEST
, "Request" },
138 {DSIFL_REPLY
, "Reply" },
141 static const value_string func_vals
[] = {
142 {DSIFUNC_CLOSE
, "CloseSession" },
143 {DSIFUNC_CMD
, "Command" },
144 {DSIFUNC_STAT
, "GetStatus" },
145 {DSIFUNC_OPEN
, "OpenSession" },
146 {DSIFUNC_TICKLE
, "Tickle" },
147 {DSIFUNC_WRITE
, "Write" },
149 {DSIFUNC_ATTN
, "Attention" },
151 static value_string_ext func_vals_ext
= VALUE_STRING_EXT_INIT(func_vals
);
154 dissect_dsi_open_session(tvbuff_t
*tvb
, proto_tree
*dsi_tree
, int offset
, int dsi_length
)
160 tree
= proto_tree_add_subtree(dsi_tree
, tvb
, offset
, -1, ett_dsi_open
, NULL
, "Open Session");
162 while( dsi_length
>2 ) {
164 type
= tvb_get_uint8(tvb
, offset
);
165 proto_tree_add_item(tree
, hf_dsi_open_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
167 len
= tvb_get_uint8(tvb
, offset
);
168 proto_tree_add_item(tree
, hf_dsi_open_len
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
172 proto_tree_add_item(tree
, hf_dsi_open_quantum
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
175 proto_tree_add_item(tree
, hf_dsi_open_quantum
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
178 proto_tree_add_item(tree
, hf_dsi_replay_cache_size
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
181 proto_tree_add_item(tree
, hf_dsi_open_option
, tvb
, offset
, len
, ENC_NA
);
184 dsi_length
-= len
+ 2;
192 dissect_dsi_attention(tvbuff_t
*tvb
, proto_tree
*dsi_tree
, int offset
)
198 if (!tvb_reported_length_remaining(tvb
,offset
))
201 flag
= tvb_get_ntohs(tvb
, offset
);
202 tree
= proto_tree_add_subtree(dsi_tree
, tvb
, offset
, -1, ett_dsi_attn
, NULL
, "Attention");
204 ti
= proto_tree_add_item(tree
, hf_dsi_attn_flag
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
205 tree
= proto_item_add_subtree(ti
, ett_dsi_attn_flag
);
206 proto_tree_add_item(tree
, hf_dsi_attn_flag_shutdown
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
207 proto_tree_add_item(tree
, hf_dsi_attn_flag_crash
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
208 proto_tree_add_item(tree
, hf_dsi_attn_flag_msg
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
209 proto_tree_add_item(tree
, hf_dsi_attn_flag_reconnect
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
211 if ((flag
& 0xf000) != 0x3000)
212 proto_tree_add_item(tree
, hf_dsi_attn_flag_time
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
214 proto_tree_add_item(tree
, hf_dsi_attn_flag_bitmap
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
220 dissect_dsi_packet(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
222 proto_tree
*dsi_tree
;
224 uint8_t dsi_flags
,dsi_command
;
225 uint16_t dsi_requestid
;
228 struct atp_asp_dsi_info atp_asp_dsi_info
;
231 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "DSI");
232 col_clear(pinfo
->cinfo
, COL_INFO
);
234 dsi_flags
= tvb_get_uint8(tvb
, 0);
235 dsi_command
= tvb_get_uint8(tvb
, 1);
236 dsi_requestid
= tvb_get_ntohs(tvb
, 2);
237 dsi_code
= tvb_get_ntohl(tvb
, 4);
238 dsi_length
= tvb_get_ntohl(tvb
, 8);
240 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "%s %s (%u)",
241 val_to_str(dsi_flags
, flag_vals
,
242 "Unknown flag (0x%02x)"),
243 val_to_str_ext(dsi_command
, &func_vals_ext
,
244 "Unknown function (0x%02x)"),
247 dsi_ti
= proto_tree_add_item(tree
, proto_dsi
, tvb
, 0, -1, ENC_NA
);
248 dsi_tree
= proto_item_add_subtree(dsi_ti
, ett_dsi
);
251 proto_tree_add_uint(dsi_tree
, hf_dsi_flags
, tvb
,
253 proto_tree_add_uint(dsi_tree
, hf_dsi_command
, tvb
,
255 proto_tree_add_uint(dsi_tree
, hf_dsi_requestid
, tvb
,
256 2, 2, dsi_requestid
);
260 proto_tree_add_int(dsi_tree
, hf_dsi_offset
, tvb
,
265 proto_tree_add_int(dsi_tree
, hf_dsi_error
, tvb
,
269 proto_tree_add_item(dsi_tree
, hf_dsi_length
, tvb
,
270 8, 4, ENC_BIG_ENDIAN
);
271 proto_tree_add_item(dsi_tree
, hf_dsi_reserved
, tvb
,
272 12, 4, ENC_BIG_ENDIAN
);
275 switch (dsi_command
) {
278 dissect_dsi_open_session(tvb
, dsi_tree
, DSI_BLOCKSIZ
, dsi_length
);
283 dissect_dsi_attention(tvb
, dsi_tree
, DSI_BLOCKSIZ
);
287 if (tree
&& (dsi_flags
== DSIFL_REPLY
)) {
290 /* XXX - assumes only AFP runs atop DSI */
291 new_tvb
= tvb_new_subset_remaining(tvb
, DSI_BLOCKSIZ
);
292 call_dissector(afp_server_status_handle
, new_tvb
, pinfo
, dsi_tree
);
300 atp_asp_dsi_info
.reply
= (dsi_flags
== DSIFL_REPLY
);
301 atp_asp_dsi_info
.tid
= dsi_requestid
;
302 atp_asp_dsi_info
.code
= dsi_code
;
303 proto_item_set_len(dsi_ti
, DSI_BLOCKSIZ
);
305 new_tvb
= tvb_new_subset_remaining(tvb
, DSI_BLOCKSIZ
);
306 call_dissector_with_data(afp_handle
, new_tvb
, pinfo
, tree
, &atp_asp_dsi_info
);
310 call_data_dissector(tvb_new_subset_remaining(tvb
, DSI_BLOCKSIZ
),
315 return tvb_captured_length(tvb
);
319 get_dsi_pdu_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
, void *data _U_
)
322 uint8_t dsi_flags
,dsi_command
;
324 dsi_flags
= tvb_get_uint8(tvb
, offset
);
325 dsi_command
= tvb_get_uint8(tvb
, offset
+ 1);
326 if ( dsi_flags
> DSIFL_MAX
|| !dsi_command
|| dsi_command
> DSIFUNC_MAX
)
328 /* it's not a known dsi pdu start sequence */
329 return tvb_captured_length_remaining(tvb
, offset
);
333 * Get the length of the DSI packet.
335 plen
= tvb_get_ntohl(tvb
, offset
+8);
338 * That length doesn't include the length of the header itself;
345 dissect_dsi(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
347 tcp_dissect_pdus(tvb
, pinfo
, tree
, dsi_desegment
, 12,
348 get_dsi_pdu_len
, dissect_dsi_packet
, data
);
350 return tvb_captured_length(tvb
);
354 proto_register_dsi(void)
357 static hf_register_info hf
[] = {
359 { "Flags", "dsi.flags",
360 FT_UINT8
, BASE_HEX
, VALS(flag_vals
), 0x0,
361 "Indicates request or reply.", HFILL
}},
364 { "Command", "dsi.command",
365 FT_UINT8
, BASE_DEC
|BASE_EXT_STRING
, &func_vals_ext
, 0x0,
366 "Represents a DSI command.", HFILL
}},
369 { "Request ID", "dsi.requestid",
370 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
371 "Keeps track of which request this is. Replies must match a Request. IDs must be generated in sequential order.", HFILL
}},
374 { "Data offset", "dsi.data_offset",
375 FT_INT32
, BASE_DEC
, NULL
, 0x0,
379 { "Error code", "dsi.error_code",
380 FT_INT32
, BASE_DEC
|BASE_EXT_STRING
, &asp_error_vals_ext
, 0x0,
384 { "Length", "dsi.length",
385 FT_UINT32
, BASE_DEC
|BASE_UNIT_STRING
, UNS(&units_byte_bytes
), 0x0,
386 "Total length of the data that follows the DSI header.", HFILL
}},
389 { "Reserved", "dsi.reserved",
390 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
391 "Reserved for future use. Should be set to zero.", HFILL
}},
394 { "Option", "dsi.open_type",
395 FT_UINT8
, BASE_DEC
, VALS(dsi_open_type_vals
), 0x0,
396 "Open session option type.", HFILL
}},
399 { "Length", "dsi.open_len",
400 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
401 "Open session option len", HFILL
}},
403 { &hf_dsi_open_quantum
,
404 { "Quantum", "dsi.open_quantum",
405 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
406 "Server/Attention quantum", HFILL
}},
408 { &hf_dsi_replay_cache_size
,
409 { "Replay", "dsi.replay_cache",
410 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
411 "Replay cache size", HFILL
}},
413 { &hf_dsi_open_option
,
414 { "Option", "dsi.open_option",
415 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
416 "Open session options (undecoded)", HFILL
}},
419 { "Flags", "dsi.attn_flag",
420 FT_UINT16
, BASE_HEX
|BASE_EXT_STRING
, &dsi_attn_flag_vals_ext
, 0xf000,
421 "Server attention flag", HFILL
}},
422 { &hf_dsi_attn_flag_shutdown
,
423 { "Shutdown", "dsi.attn_flag.shutdown",
424 FT_BOOLEAN
, 16, NULL
, 1<<15,
425 "Attention flag, server is shutting down", HFILL
}},
426 { &hf_dsi_attn_flag_crash
,
427 { "Crash", "dsi.attn_flag.crash",
428 FT_BOOLEAN
, 16, NULL
, 1<<14,
429 "Attention flag, server crash bit", HFILL
}},
430 { &hf_dsi_attn_flag_msg
,
431 { "Message", "dsi.attn_flag.msg",
432 FT_BOOLEAN
, 16, NULL
, 1<<13,
433 "Attention flag, server message bit", HFILL
}},
434 { &hf_dsi_attn_flag_reconnect
,
435 { "Don't reconnect", "dsi.attn_flag.reconnect",
436 FT_BOOLEAN
, 16, NULL
, 1<<12,
437 "Attention flag, don't reconnect bit", HFILL
}},
438 { &hf_dsi_attn_flag_time
,
439 { "Minutes", "dsi.attn_flag.time",
440 FT_UINT16
, BASE_DEC
, NULL
, 0x0fff,
441 "Number of minutes", HFILL
}},
442 { &hf_dsi_attn_flag_bitmap
,
443 { "Bitmap", "dsi.attn_flag.bitmap",
444 FT_UINT16
, BASE_HEX
, NULL
, 0x0fff,
445 "Attention extended bitmap", HFILL
}},
448 static int *ett
[] = {
454 module_t
*dsi_module
;
456 proto_dsi
= proto_register_protocol("Data Stream Interface", "DSI", "dsi");
457 proto_register_field_array(proto_dsi
, hf
, array_length(hf
));
458 proto_register_subtree_array(ett
, array_length(ett
));
460 dsi_module
= prefs_register_protocol(proto_dsi
, NULL
);
461 prefs_register_bool_preference(dsi_module
, "desegment",
462 "Reassemble DSI messages spanning multiple TCP segments",
463 "Whether the DSI dissector should reassemble messages spanning multiple TCP segments."
464 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
467 dsi_handle
= register_dissector("dsi", dissect_dsi
, proto_dsi
);
471 proto_reg_handoff_dsi(void)
473 dissector_add_uint_with_preference("tcp.port", TCP_PORT_DSI
, dsi_handle
);
475 afp_handle
= find_dissector_add_dependency("afp", proto_dsi
);
476 afp_server_status_handle
= find_dissector_add_dependency("afp_server_status", proto_dsi
);
480 * Editor modelines - https://www.wireshark.org/tools/modelines.html
485 * indent-tabs-mode: t
488 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
489 * :indentSize=8:tabSize=8:noTabs=false: