Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-applemidi.c
blob8637092c56b5c4dc023057dde88125f018e75bfc
1 /* packet-applemidi.c
2 * Routines for dissection of Apple network-midi session establishment.
3 * Copyright 2006-2012, Tobias Erichsen <t.erichsen@gmx.de>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * Copied from packet-data.c, README.developer, and various other files.
11 * SPDX-License-Identifier: GPL-2.0-or-later
14 * Apple network-midi session establishment is a lightweight protocol for
15 * providing a simple session establishment for MIDI-data sent in the form
16 * of RTP-MIDI (RFC 4695 / 6295). Peers recognize each other using the
17 * Apple Bonjour scheme with the service-name "_apple-midi._udp", establish
18 * a connection using AppleMIDI (no official name, just an abbreviation)
19 * and then send payload using RTP-MIDI. The implementation of this
20 * dissector is based on the Apple implementation summary from May 6th, 2005
21 * and the extension from August 13th, 2010.
23 * 2010-11-29
24 * - initial version of dissector
25 * 2012-02-24
26 * - implemented dynamic payloadtype support to automatically punt
27 * the decoding to the RTP-MIDI dissector via the RTP dissector
28 * - added new bitrate receive limit feature
30 * Here are some links:
32 * http://www.cs.berkeley.edu/~lazzaro/rtpmidi/
33 * https://tools.ietf.org/html/rfc4695
34 * https://tools.ietf.org/html/rfc6925
37 #include "config.h"
39 #include <epan/packet.h>
40 #include <epan/conversation.h>
42 #include "packet-rtp.h"
44 void proto_register_applemidi(void);
45 void proto_reg_handoff_applemidi(void);
47 /* Definitions for protocol name during dissector-register */
48 #define APPLEMIDI_DISSECTOR_NAME "Apple Network-MIDI Session Protocol"
49 #define APPLEMIDI_DISSECTOR_SHORTNAME "AppleMIDI"
50 #define APPLEMIDI_DISSECTOR_ABBREVIATION "applemidi"
52 /* Signature "Magic Value" for Apple network MIDI session establishment */
53 #define APPLEMIDI_PROTOCOL_SIGNATURE 0xffff
55 /* Apple network MIDI valid commands */
56 #define APPLEMIDI_COMMAND_INVITATION 0x494e /* "IN" */
57 #define APPLEMIDI_COMMAND_INVITATION_REJECTED 0x4e4f /* "NO" */
58 #define APLLEMIDI_COMMAND_INVITATION_ACCEPTED 0x4f4b /* "OK" */
59 #define APPLEMIDI_COMMAND_ENDSESSION 0x4259 /* "BY" */
60 #define APPLEMIDI_COMMAND_SYNCHRONIZATION 0x434b /* "CK" */
61 #define APPLEMIDI_COMMAND_RECEIVER_FEEDBACK 0x5253 /* "RS" */
62 #define APPLEMIDI_COMMAND_BITRATE_RECEIVE_LIMIT 0x524c /* "RL" */
64 static int hf_applemidi_signature;
65 static int hf_applemidi_command;
66 static int hf_applemidi_protocol_version;
67 static int hf_applemidi_token;
68 static int hf_applemidi_ssrc;
69 static int hf_applemidi_name;
70 static int hf_applemidi_count;
71 static int hf_applemidi_padding;
72 static int hf_applemidi_timestamp1;
73 static int hf_applemidi_timestamp2;
74 static int hf_applemidi_timestamp3;
75 static int hf_applemidi_sequence_num;
76 static int hf_applemidi_rtp_sequence_num;
77 static int hf_applemidi_rtp_bitrate_limit;
78 static int hf_applemidi_unknown_data;
81 static int ett_applemidi;
82 static int ett_applemidi_seq_num;
85 static const value_string applemidi_commands[] = {
86 { APPLEMIDI_COMMAND_INVITATION, "Invitation" },
87 { APPLEMIDI_COMMAND_INVITATION_REJECTED, "Invitation Rejected" },
88 { APLLEMIDI_COMMAND_INVITATION_ACCEPTED, "Invitation Accepted" },
89 { APPLEMIDI_COMMAND_ENDSESSION, "End Session" },
90 { APPLEMIDI_COMMAND_SYNCHRONIZATION, "Synchronization" },
91 { APPLEMIDI_COMMAND_RECEIVER_FEEDBACK, "Receiver Feedback" },
92 { APPLEMIDI_COMMAND_BITRATE_RECEIVE_LIMIT, "Bitrate Receive Limit" },
93 { 0, NULL },
97 static int proto_applemidi;
99 static dissector_handle_t applemidi_handle;
100 static dissector_handle_t rtp_handle;
102 static const char applemidi_unknown_command[] = "unknown command: 0x%04x";
104 static void
105 dissect_applemidi_common( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, uint16_t command ) {
107 proto_item *ti;
108 uint16_t seq_num;
109 uint8_t count;
110 uint8_t *name;
111 int offset = 0;
112 int len;
113 int string_size;
114 proto_tree *applemidi_tree;
115 proto_tree *applemidi_tree_seq_num;
118 col_set_str( pinfo->cinfo, COL_PROTOCOL, APPLEMIDI_DISSECTOR_SHORTNAME );
120 col_add_str( pinfo->cinfo, COL_INFO, val_to_str( command, applemidi_commands, applemidi_unknown_command ) );
122 ti = proto_tree_add_item( tree, proto_applemidi, tvb, 0, -1, ENC_NA );
123 applemidi_tree = proto_item_add_subtree( ti, ett_applemidi );
125 proto_tree_add_item( applemidi_tree, hf_applemidi_signature, tvb, offset, 2, ENC_BIG_ENDIAN );
126 offset += 2;
128 proto_tree_add_item( applemidi_tree, hf_applemidi_command, tvb, offset, 2, ENC_BIG_ENDIAN );
129 offset += 2;
131 /* the format of packets for "IN", "NO", "OK" and "BY" is identical and contains
132 * the protocol version, a random number generated by the initiator of the session,
133 * the SSRC that is used by the respective sides RTP-entity and optionally the
134 * name of the participant */
135 if ( ( APPLEMIDI_COMMAND_INVITATION == command ) ||
136 ( APPLEMIDI_COMMAND_INVITATION_REJECTED == command ) ||
137 ( APLLEMIDI_COMMAND_INVITATION_ACCEPTED == command ) ||
138 ( APPLEMIDI_COMMAND_ENDSESSION == command ) ) {
140 proto_tree_add_item( applemidi_tree, hf_applemidi_protocol_version, tvb, offset, 4, ENC_BIG_ENDIAN );
141 offset += 4;
143 proto_tree_add_item( applemidi_tree, hf_applemidi_token, tvb, offset, 4, ENC_BIG_ENDIAN );
144 offset += 4;
146 proto_tree_add_item( applemidi_tree, hf_applemidi_ssrc, tvb, offset, 4, ENC_BIG_ENDIAN );
147 offset += 4;
149 len = tvb_reported_length(tvb) - offset;
151 /* Name is optional */
152 if ( len > 0 ) {
153 name = tvb_get_string_enc( pinfo->pool, tvb, offset, len, ENC_UTF_8|ENC_NA );
154 string_size = (int)( strlen( name ) + 1 );
155 proto_tree_add_item( applemidi_tree, hf_applemidi_name, tvb, offset, string_size, ENC_UTF_8 );
156 col_append_fstr( pinfo->cinfo, COL_INFO, ": peer = \"%s\"", name );
157 offset += string_size;
160 /* the synchronization packet contains three 64bit timestamps, and a value to define how
161 * many of the timestamps transmitted are valid */
162 } else if ( APPLEMIDI_COMMAND_SYNCHRONIZATION == command ) {
163 proto_tree_add_item( applemidi_tree, hf_applemidi_ssrc, tvb, offset, 4, ENC_BIG_ENDIAN );
164 offset += 4;
166 count = tvb_get_uint8( tvb, offset );
167 proto_tree_add_item( applemidi_tree, hf_applemidi_count, tvb, offset, 1, ENC_BIG_ENDIAN );
168 col_append_fstr( pinfo->cinfo, COL_INFO, ": count = %u", count );
169 offset += 1;
171 proto_tree_add_item( applemidi_tree, hf_applemidi_padding, tvb, offset, 3, ENC_BIG_ENDIAN );
172 offset += 3;
174 proto_tree_add_item( applemidi_tree, hf_applemidi_timestamp1, tvb, offset, 8, ENC_BIG_ENDIAN );
175 offset += 8;
177 proto_tree_add_item( applemidi_tree, hf_applemidi_timestamp2, tvb, offset, 8, ENC_BIG_ENDIAN );
178 offset += 8;
180 proto_tree_add_item( applemidi_tree, hf_applemidi_timestamp3, tvb, offset, 8, ENC_BIG_ENDIAN );
181 offset += 8;
182 /* With the receiver feedback packet, the recipient can tell the sender up to what sequence
183 * number in the RTP-stream the packets have been received; this can be used to shorten the
184 * recovery-journal-section in the RTP-session */
185 } else if ( APPLEMIDI_COMMAND_RECEIVER_FEEDBACK == command ) {
186 proto_tree_add_item( applemidi_tree, hf_applemidi_ssrc, tvb, offset, 4, ENC_BIG_ENDIAN );
187 offset += 4;
189 ti = proto_tree_add_item( applemidi_tree, hf_applemidi_sequence_num, tvb, offset, 4, ENC_BIG_ENDIAN );
190 /* Apple includes a 32bit sequence-number, but the RTP-packet only specifies 16bit.
191 * this subtree and subitem are added to be able to associate the sequence-number
192 * here easier with the one specified in the corresponding RTP-packet */
193 applemidi_tree_seq_num = proto_item_add_subtree( ti, ett_applemidi_seq_num );
194 seq_num = tvb_get_ntohs( tvb, offset );
195 proto_tree_add_uint( applemidi_tree_seq_num, hf_applemidi_rtp_sequence_num, tvb, offset, 2, seq_num );
196 offset += 4;
198 col_append_fstr( pinfo->cinfo, COL_INFO, ": seq = %u", seq_num );
199 /* With the bitrate receive limit packet, the recipient can tell the sender to limit
200 the transmission to a certain bitrate. This is important if the peer is a gateway
201 to a hardware-device that only supports a certain speed. Like the MIDI 1.0 DIN-cable
202 MIDI-implementation which is limited to 31250. */
203 } else if ( APPLEMIDI_COMMAND_BITRATE_RECEIVE_LIMIT == command ) {
204 proto_tree_add_item( applemidi_tree, hf_applemidi_ssrc, tvb, offset, 4, ENC_BIG_ENDIAN );
205 offset += 4;
207 proto_tree_add_item( applemidi_tree, hf_applemidi_rtp_bitrate_limit,
208 tvb, offset, 4, ENC_BIG_ENDIAN );
209 offset += 4;
211 /* If there is any remaining data (possibly because an unknown command was encountered),
212 * we just dump it here */
213 len = tvb_reported_length_remaining( tvb, offset );
214 if ( len > 0 ) {
215 proto_tree_add_item( applemidi_tree, hf_applemidi_unknown_data, tvb, offset, len, ENC_NA );
219 static bool
220 test_applemidi(tvbuff_t *tvb, uint16_t *command_p, bool conversation_established ) {
222 *command_p = 0xffff;
224 /* An applemidi session protocol UDP-packet must start with the "magic value" of 0xffff ... */
225 if ( APPLEMIDI_PROTOCOL_SIGNATURE != tvb_get_ntohs( tvb, 0 ) )
226 return false;
228 *command_p = tvb_get_ntohs( tvb, 2 );
230 /* If the conversation is establised (one prior packet with a valid known command)
231 * we won't check the commands anymore - this way we still show new commands
232 * Apple might introduce as "unknown" instead of punting to RTP-dissector */
233 if ( conversation_established ) {
234 return true;
238 /* ... followed by packet-command: "IN", "NO", "OK", "BY", "CK" and "RS" and "RL" */
239 if ( ( APPLEMIDI_COMMAND_INVITATION == *command_p ) ||
240 ( APPLEMIDI_COMMAND_INVITATION_REJECTED == *command_p ) ||
241 ( APLLEMIDI_COMMAND_INVITATION_ACCEPTED == *command_p ) ||
242 ( APPLEMIDI_COMMAND_ENDSESSION == *command_p ) ||
243 ( APPLEMIDI_COMMAND_SYNCHRONIZATION == *command_p ) ||
244 ( APPLEMIDI_COMMAND_RECEIVER_FEEDBACK == *command_p ) ||
245 ( APPLEMIDI_COMMAND_BITRATE_RECEIVE_LIMIT == *command_p ) )
246 return true;
248 return false;
253 /* dissect_applemidi() is called when a packet is seen from a previously identified applemidi conversation */
254 /* If the packet isn't a valid applemidi packet, assume it's an RTP-MIDI packet. */
256 static int
257 dissect_applemidi( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_ ) {
258 uint16_t command;
260 if ( test_applemidi( tvb, &command, true ) )
261 dissect_applemidi_common( tvb, pinfo, tree, command );
262 else
263 call_dissector( rtp_handle, tvb, pinfo, tree );
265 return tvb_captured_length(tvb);
268 static bool
269 dissect_applemidi_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_ ) {
271 uint16_t command;
272 conversation_t *p_conv;
273 rtp_dyn_payload_t *rtp_dyn_payload;
275 if ( tvb_captured_length( tvb ) < 4)
276 return false; /* not enough bytes to check */
278 if ( !test_applemidi( tvb, &command, false ) ) {
279 return false;
282 /* set dynamic payload-type 97 which is used by Apple for their RTP-MIDI implementation for this
283 address/port-tuple to cause RTP-dissector to call the RTP-MIDI-dissector for payload-decoding */
285 rtp_dyn_payload = rtp_dyn_payload_new();
286 rtp_dyn_payload_insert(rtp_dyn_payload, 97, "rtp-midi", 10000, 1);
287 rtp_add_address( pinfo, PT_UDP, &pinfo->src, pinfo->srcport, 0, APPLEMIDI_DISSECTOR_SHORTNAME,
288 pinfo->num, false, rtp_dyn_payload);
290 /* call dissect_applemidi() from now on for UDP packets on this "connection"
291 it is important to do this step after calling rtp_add_address, otherwise
292 all further packets will go directly to the RTP-dissector! */
294 p_conv = find_or_create_conversation(pinfo);
295 conversation_set_dissector( p_conv, applemidi_handle );
297 /* punt to actual decoding */
299 dissect_applemidi_common( tvb, pinfo, tree, command );
300 return true;
305 void
306 proto_register_applemidi( void )
308 static hf_register_info hf[] = {
310 &hf_applemidi_signature,
312 "Signature",
313 "applemidi.signature",
314 FT_UINT16,
315 BASE_HEX,
316 NULL,
317 0x0,
318 NULL, HFILL
322 &hf_applemidi_command,
324 "Command",
325 "applemidi.command",
326 FT_UINT16,
327 BASE_HEX,
328 VALS( applemidi_commands ),
329 0x0,
330 NULL, HFILL
334 &hf_applemidi_protocol_version,
336 "Protocol Version",
337 "applemidi.protocol_version",
338 FT_UINT32,
339 BASE_DEC,
340 NULL,
341 0x0,
342 NULL, HFILL
346 &hf_applemidi_token,
348 "Initiator Token",
349 "applemidi.initiator_token",
350 FT_UINT32,
351 BASE_HEX,
352 NULL,
353 0x0,
354 NULL, HFILL
358 &hf_applemidi_ssrc,
360 "Sender SSRC",
361 "applemidi.sender_ssrc",
362 FT_UINT32,
363 BASE_HEX,
364 NULL,
365 0x0,
366 NULL, HFILL
370 &hf_applemidi_name,
372 "Name",
373 "applemidi.name",
374 FT_STRING,
375 BASE_NONE,
376 NULL,
377 0x0,
378 NULL, HFILL
382 &hf_applemidi_count,
384 "Count",
385 "applemidi.count",
386 FT_UINT8,
387 BASE_DEC,
388 NULL,
389 0x0,
390 NULL, HFILL
394 &hf_applemidi_padding,
396 "Padding",
397 "applemidi.padding",
398 FT_UINT24,
399 BASE_HEX,
400 NULL,
401 0x0,
402 NULL, HFILL
406 &hf_applemidi_timestamp1,
408 "Timestamp 1",
409 "applemidi.timestamp1",
410 FT_UINT64,
411 BASE_HEX,
412 NULL,
413 0x0,
414 NULL, HFILL
418 &hf_applemidi_timestamp2,
420 "Timestamp 2",
421 "applemidi.timestamp2",
422 FT_UINT64,
423 BASE_HEX,
424 NULL,
425 0x0,
426 NULL, HFILL
430 &hf_applemidi_timestamp3,
432 "Timestamp 3",
433 "applemidi.timestamp3",
434 FT_UINT64,
435 BASE_HEX,
436 NULL,
437 0x0,
438 NULL, HFILL
442 &hf_applemidi_sequence_num,
444 "Sequence Number",
445 "applemidi.sequence_number",
446 FT_UINT32,
447 BASE_HEX,
448 NULL,
449 0x0,
450 NULL, HFILL
454 &hf_applemidi_rtp_sequence_num,
456 "RTP Sequence Number",
457 "applemidi.rtp_sequence_number",
458 FT_UINT16,
459 BASE_DEC,
460 NULL,
461 0x0,
462 NULL, HFILL
466 &hf_applemidi_rtp_bitrate_limit,
468 "Bitrate limit",
469 "applemidi.bitrate_limit",
470 FT_UINT32,
471 BASE_DEC,
472 NULL,
473 0x0,
474 NULL, HFILL
478 &hf_applemidi_unknown_data,
480 "Unknown Data",
481 "applemidi.unknown_data",
482 FT_BYTES,
483 BASE_NONE,
484 NULL,
485 0x00,
486 NULL, HFILL
492 static int *ett[] = {
493 &ett_applemidi,
494 &ett_applemidi_seq_num
497 proto_applemidi = proto_register_protocol( APPLEMIDI_DISSECTOR_NAME,
498 APPLEMIDI_DISSECTOR_SHORTNAME,
499 APPLEMIDI_DISSECTOR_ABBREVIATION );
500 proto_register_field_array( proto_applemidi, hf, array_length( hf ) );
501 proto_register_subtree_array( ett, array_length( ett ) );
503 applemidi_handle = register_dissector( "applemidi", dissect_applemidi, proto_applemidi );
506 void
507 proto_reg_handoff_applemidi( void ) {
508 /* If we cannot decode the data it will be RTP-MIDI since the Apple session protocol uses
509 * two ports: the control-port and the MIDI-port. On both ports an invitation is being sent.
510 * The second port is then used for the RTP-MIDI-data. So if we can't find valid AppleMidi
511 * packets, it will be most likely RTP-MIDI...
513 rtp_handle = find_dissector_add_dependency( "rtp", proto_applemidi );
514 heur_dissector_add( "udp", dissect_applemidi_heur, "Apple MIDI over UDP", "applemidi_udp", proto_applemidi, HEURISTIC_ENABLE );
518 * Editor modelines - https://www.wireshark.org/tools/modelines.html
520 * Local variables:
521 * c-basic-offset: 8
522 * tab-width: 8
523 * indent-tabs-mode: t
524 * End:
526 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
527 * :indentSize=8:tabSize=8:noTabs=false: