Fixup: tvb_get_string(z) -> tvb_get_string(z)_enc
[metze/wireshark/wip.git] / epan / dissectors / packet-applemidi.c
1 /* packet-applemidi.c
2  * Routines for dissection of Apple network-midi session establishment.
3  * Copyright 2006-2012, Tobias Erichsen <t.erichsen@gmx.de>
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * Copied from packet-data.c, README.developer, and various other files.
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  * Apple network-midi session establishment is a lightweight protocol for
27  * providing a simple session establishment for MIDI-data sent in the form
28  * of RTP-MIDI (RFC 4695 / 6295).  Peers recognize each other using the
29  * Apple Bonjour scheme with the service-name "_apple-midi._udp", establish
30  * a connection using AppleMIDI (no official name, just an abbreviation)
31  * and then send payload using RTP-MIDI.  The implementation of this
32  * dissector is based on the Apple implementation summary from May 6th, 2005
33  * and the extension from August 13th, 2010.
34  *
35  * 2010-11-29
36  * - initial version of dissector
37  * 2012-02-24
38  * - implemented dynamic payloadtype support to automatically punt
39  *   the decoding to the RTP-MIDI dissector via the RTP dissector
40  * - added new bitrate receive limit feature
41  *
42  * Here are some links:
43  *
44  * http://www.cs.berkeley.edu/~lazzaro/rtpmidi/
45  * http://www.faqs.org/rfcs/rfc4695.html
46  * http://www.faqs.org/rfcs/rfc6295.html
47  */
48
49 #include "config.h"
50
51 #include <glib.h>
52 #include <epan/packet.h>
53 #include <epan/wmem/wmem.h>
54 #include <epan/conversation.h>
55
56 #include "packet-rtp.h"
57
58 void proto_register_applemidi(void);
59 void proto_reg_handoff_applemidi(void);
60
61 /* Definitions for protocol name during dissector-register */
62 #define APPLEMIDI_DISSECTOR_NAME                        "Apple Network-MIDI Session Protocol"
63 #define APPLEMIDI_DISSECTOR_SHORTNAME                   "AppleMIDI"
64 #define APPLEMIDI_DISSECTOR_ABBREVIATION                "applemidi"
65
66 /* Signature "Magic Value" for Apple network MIDI session establishment */
67 #define APPLEMIDI_PROTOCOL_SIGNATURE                    0xffff
68
69 /* Apple network MIDI valid commands */
70 #define APPLEMIDI_COMMAND_INVITATION                    0x494e          /*   "IN"   */
71 #define APPLEMIDI_COMMAND_INVITATION_REJECTED           0x4e4f          /*   "NO"   */
72 #define APLLEMIDI_COMMAND_INVITATION_ACCEPTED           0x4f4b          /*   "OK"   */
73 #define APPLEMIDI_COMMAND_ENDSESSION                    0x4259          /*   "BY"   */
74 #define APPLEMIDI_COMMAND_SYNCHRONIZATION               0x434b          /*   "CK"   */
75 #define APPLEMIDI_COMMAND_RECEIVER_FEEDBACK             0x5253          /*   "RS"   */
76 #define APPLEMIDI_COMMAND_BITRATE_RECEIVE_LIMIT         0x524c          /*   "RL"   */
77
78 static int      hf_applemidi_signature                  = -1;
79 static int      hf_applemidi_command                    = -1;
80 static int      hf_applemidi_protocol_version           = -1;
81 static int      hf_applemidi_token                      = -1;
82 static int      hf_applemidi_ssrc                       = -1;
83 static int      hf_applemidi_name                       = -1;
84 static int      hf_applemidi_count                      = -1;
85 static int      hf_applemidi_padding                    = -1;
86 static int      hf_applemidi_timestamp1                 = -1;
87 static int      hf_applemidi_timestamp2                 = -1;
88 static int      hf_applemidi_timestamp3                 = -1;
89 static int      hf_applemidi_sequence_num               = -1;
90 static int      hf_applemidi_rtp_sequence_num           = -1;
91 static int      hf_applemidi_rtp_bitrate_limit          = -1;
92 static int      hf_applemidi_unknown_data               = -1;
93
94
95 static gint     ett_applemidi                           = -1;
96 static gint     ett_applemidi_seq_num                   = -1;
97
98
99 static const value_string applemidi_commands[] = {
100         { APPLEMIDI_COMMAND_INVITATION,                 "Invitation" },
101         { APPLEMIDI_COMMAND_INVITATION_REJECTED,        "Invitation Rejected" },
102         { APLLEMIDI_COMMAND_INVITATION_ACCEPTED,        "Invitation Accepted" },
103         { APPLEMIDI_COMMAND_ENDSESSION,                 "End Session" },
104         { APPLEMIDI_COMMAND_SYNCHRONIZATION,            "Synchronization" },
105         { APPLEMIDI_COMMAND_RECEIVER_FEEDBACK,          "Receiver Feedback" },
106         { APPLEMIDI_COMMAND_BITRATE_RECEIVE_LIMIT,      "Bitrate Receive Limit" },
107         { 0,                                            NULL },
108 };
109
110
111 static int                      proto_applemidi         = -1;
112
113 static dissector_handle_t       applemidi_handle;
114 static dissector_handle_t       rtp_handle;
115
116 static const char applemidi_unknown_command[]           = "unknown command: 0x%04x";
117
118 static void
119 dissect_applemidi_common( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint16 command ) {
120
121         guint16          seq_num;
122         guint8           count;
123         guint8          *name;
124         gint             offset                 = 0;
125         gint             len;
126         gint             string_size;
127         proto_tree      *applemidi_tree;
128         proto_tree      *applemidi_tree_seq_num;
129
130
131         col_set_str( pinfo->cinfo, COL_PROTOCOL, APPLEMIDI_DISSECTOR_SHORTNAME );
132
133         col_add_fstr( pinfo->cinfo, COL_INFO, "%s", val_to_str( command, applemidi_commands, applemidi_unknown_command ) );
134
135         if ( tree ) {
136                 proto_item *ti;
137                 ti = proto_tree_add_item( tree, proto_applemidi, tvb, 0, -1, ENC_NA  );
138                 applemidi_tree = proto_item_add_subtree( ti, ett_applemidi );
139
140                 proto_tree_add_item( applemidi_tree, hf_applemidi_signature, tvb, offset, 2, ENC_BIG_ENDIAN  );
141                 offset += 2;
142
143                 proto_tree_add_item( applemidi_tree, hf_applemidi_command, tvb, offset, 2, ENC_BIG_ENDIAN  );
144                 offset += 2;
145
146                 /* the format of packets for "IN", "NO", "OK" and "BY" is identical and contains
147                  * the protocol version, a random number generated by the initiator of the session,
148                  * the SSRC that is used by the respective sides RTP-entity and optionally the
149                  * name of the participant */
150                 if ( ( APPLEMIDI_COMMAND_INVITATION == command ) ||
151                      ( APPLEMIDI_COMMAND_INVITATION_REJECTED == command ) ||
152                      ( APLLEMIDI_COMMAND_INVITATION_ACCEPTED == command ) ||
153                      ( APPLEMIDI_COMMAND_ENDSESSION == command ) ) {
154
155                         proto_tree_add_item( applemidi_tree, hf_applemidi_protocol_version, tvb, offset, 4, ENC_BIG_ENDIAN  );
156                         offset += 4;
157
158                         proto_tree_add_item( applemidi_tree, hf_applemidi_token, tvb, offset, 4, ENC_BIG_ENDIAN  );
159                         offset += 4;
160
161                         proto_tree_add_item( applemidi_tree, hf_applemidi_ssrc, tvb, offset, 4, ENC_BIG_ENDIAN  );
162                         offset += 4;
163
164                         len = tvb_reported_length(tvb) - offset;
165
166                         /* Name is optional */
167                         if ( len > 0 ) {
168                                 name = tvb_get_string_enc( wmem_packet_scope(), tvb, offset, len, ENC_UTF_8|ENC_NA );
169                                 string_size = (gint)( strlen( name ) + 1 );
170                                 proto_tree_add_item( applemidi_tree, hf_applemidi_name, tvb, offset, string_size, ENC_UTF_8|ENC_NA );
171                                 col_append_fstr( pinfo->cinfo, COL_INFO, ": peer = \"%s\"", name );
172                                 offset += string_size;
173                         }
174
175                 /* the synchronization packet contains three 64bit timestamps,  and a value to define how
176                  * many of the timestamps transmitted are valid */
177                 } else if ( APPLEMIDI_COMMAND_SYNCHRONIZATION == command ) {
178                         proto_tree_add_item( applemidi_tree, hf_applemidi_ssrc, tvb, offset, 4, ENC_BIG_ENDIAN );
179                         offset += 4;
180
181                         count = tvb_get_guint8( tvb, offset );
182                         proto_tree_add_item( applemidi_tree, hf_applemidi_count, tvb, offset, 1, ENC_BIG_ENDIAN );
183                         col_append_fstr( pinfo->cinfo, COL_INFO, ": count = %u", count );
184                         offset += 1;
185
186                         proto_tree_add_item( applemidi_tree, hf_applemidi_padding, tvb, offset, 3, ENC_BIG_ENDIAN );
187                         offset += 3;
188
189                         proto_tree_add_item( applemidi_tree, hf_applemidi_timestamp1, tvb, offset, 8, ENC_BIG_ENDIAN );
190                         offset += 8;
191
192                         proto_tree_add_item( applemidi_tree, hf_applemidi_timestamp2, tvb, offset, 8, ENC_BIG_ENDIAN );
193                         offset += 8;
194
195                         proto_tree_add_item( applemidi_tree, hf_applemidi_timestamp3, tvb, offset, 8, ENC_BIG_ENDIAN );
196                         offset += 8;
197                 /* With the receiver feedback packet, the recipient can tell the sender up to what sequence
198                  * number in the RTP-stream the packets have been received; this can be used to shorten the
199                  * recovery-journal-section in the RTP-session */
200                 } else if ( APPLEMIDI_COMMAND_RECEIVER_FEEDBACK == command ) {
201                         proto_tree_add_item( applemidi_tree, hf_applemidi_ssrc, tvb, offset, 4, ENC_BIG_ENDIAN );
202                         offset += 4;
203
204                         ti = proto_tree_add_item( applemidi_tree, hf_applemidi_sequence_num, tvb, offset, 4, ENC_BIG_ENDIAN );
205                         /* Apple includes a 32bit sequence-number, but the RTP-packet only specifies 16bit.
206                          * this subtree and subitem are added to be able to associate the sequence-number
207                          * here easier with the one specified in the corresponding RTP-packet */
208                         applemidi_tree_seq_num = proto_item_add_subtree( ti, ett_applemidi_seq_num );
209                         seq_num = tvb_get_ntohs( tvb, offset );
210                         proto_tree_add_uint( applemidi_tree_seq_num, hf_applemidi_rtp_sequence_num, tvb, offset, 2, seq_num );
211                         offset += 4;
212
213                         col_append_fstr( pinfo->cinfo, COL_INFO, ": seq = %u", seq_num );
214                 /* With the bitrate receive limit packet, the recipient can tell the sender to limit
215                    the transmission to a certain bitrate.  This is important if the peer is a gateway
216                    to a hardware-device that only supports a certain speed.  Like the MIDI 1.0 DIN-cable
217                    MIDI-implementation which is limited to 31250.  */
218                 } else if ( APPLEMIDI_COMMAND_BITRATE_RECEIVE_LIMIT == command ) {
219                         proto_tree_add_item( applemidi_tree, hf_applemidi_ssrc, tvb, offset, 4, ENC_BIG_ENDIAN );
220                         offset += 4;
221
222                         proto_tree_add_item( applemidi_tree, hf_applemidi_rtp_bitrate_limit,
223                                              tvb, offset, 4, ENC_BIG_ENDIAN );
224                         offset += 4;
225                 }
226                 /* If there is any remaining data (possibly because an unknown command was encountered),
227                  * we just dump it here */
228                 len = tvb_length_remaining( tvb, offset );
229                 if ( len > 0 ) {
230                         proto_tree_add_item( applemidi_tree, hf_applemidi_unknown_data, tvb, offset, len, ENC_NA );
231                 }
232         }
233 }
234
235 static gboolean
236 test_applemidi(tvbuff_t *tvb, guint16 *command_p, gboolean conversation_established ) {
237
238         *command_p = 0xffff;
239
240         /* An applemidi session protocol UDP-packet must start with the "magic value" of 0xffff ... */
241         if ( APPLEMIDI_PROTOCOL_SIGNATURE != tvb_get_ntohs( tvb, 0 ) )
242                 return FALSE;
243
244         *command_p = tvb_get_ntohs( tvb, 2 );
245
246         /* If the conversation is establised (one prior packet with a valid known command)
247          * we won't check the commands anymore - this way we still show new commands
248          * Apple might introduct as "unknown" instead of punting to RTP-dissector */
249         if ( conversation_established ) {
250                 return TRUE;
251         }
252
253
254         /* ... followed by packet-command: "IN", "NO", "OK", "BY", "CK" and "RS" and "RL" */
255         if ( ( APPLEMIDI_COMMAND_INVITATION            == *command_p ) ||
256              ( APPLEMIDI_COMMAND_INVITATION_REJECTED   == *command_p ) ||
257              ( APLLEMIDI_COMMAND_INVITATION_ACCEPTED   == *command_p ) ||
258              ( APPLEMIDI_COMMAND_ENDSESSION            == *command_p ) ||
259              ( APPLEMIDI_COMMAND_SYNCHRONIZATION       == *command_p ) ||
260              ( APPLEMIDI_COMMAND_RECEIVER_FEEDBACK     == *command_p ) ||
261              ( APPLEMIDI_COMMAND_BITRATE_RECEIVE_LIMIT == *command_p ) )
262                 return TRUE;
263
264         return FALSE;
265 }
266
267
268
269 /* dissect_applemidi() is called when a packet is seen from a previously identified applemidi conversation */
270 /*  If the packet isn't a valid applemidi packet, assume it's an RTP-MIDI packet.                          */
271
272 static void
273 dissect_applemidi( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ) {
274         guint16         command;
275
276         if ( test_applemidi( tvb, &command, TRUE ) )
277                 dissect_applemidi_common( tvb, pinfo, tree, command );
278         else
279                 call_dissector( rtp_handle, tvb, pinfo, tree );
280 }
281
282 static gboolean
283 dissect_applemidi_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_ ) {
284
285         guint16          command;
286         conversation_t  *p_conv;
287         /*struct _rtp_conversation_info *p_conv_data = NULL;*/
288         rtp_dyn_payload_t *rtp_dyn_payload = NULL;
289
290         if ( tvb_length( tvb ) < 4)
291                 return FALSE;  /* not enough bytes to check */
292
293         if ( !test_applemidi( tvb, &command, FALSE ) ) {
294                 return FALSE;
295         }
296
297         /* set dynamic payload-type 97 which is used by Apple for their RTP-MIDI implementation for this
298            address/port-tuple to cause RTP-dissector to call the RTP-MIDI-dissector for payload-decoding */
299
300         rtp_dyn_payload = rtp_dyn_payload_new();
301         rtp_dyn_payload_insert(rtp_dyn_payload, 97, "rtp-midi", 10000);
302         rtp_add_address( pinfo, &pinfo->src, pinfo->srcport, 0, APPLEMIDI_DISSECTOR_SHORTNAME,
303                          pinfo->fd->num, FALSE, rtp_dyn_payload);
304
305         /* call dissect_applemidi() from now on for UDP packets on this "connection"
306            it is important to do this step after calling rtp_add_address, otherwise
307            all further packets will go directly to the RTP-dissector!                */
308
309         p_conv = find_or_create_conversation(pinfo);
310         conversation_set_dissector( p_conv, applemidi_handle );
311
312         /* punt to actual decoding */
313
314         dissect_applemidi_common( tvb, pinfo, tree, command );
315         return TRUE;
316
317 }
318
319
320 void
321 proto_register_applemidi( void )
322 {
323         static hf_register_info hf[] =  {
324                 {
325                         &hf_applemidi_signature,
326                         {
327                                 "Signature",
328                                 "applemidi.signature",
329                                 FT_UINT16,
330                                 BASE_HEX,
331                                 NULL,
332                                 0x0,
333                                 NULL, HFILL
334                         }
335                 },
336                 {
337                         &hf_applemidi_command,
338                         {
339                                 "Command",
340                                 "applemidi.command",
341                                 FT_UINT16,
342                                 BASE_HEX,
343                                 VALS( applemidi_commands ),
344                                 0x0,
345                                 NULL, HFILL
346                         }
347                 },
348                 {
349                         &hf_applemidi_protocol_version,
350                         {
351                                 "Protocol Version",
352                                 "applemidi.protocol_version",
353                                 FT_UINT32,
354                                 BASE_DEC,
355                                 NULL,
356                                 0x0,
357                                 NULL, HFILL
358                         }
359                 },
360                 {
361                         &hf_applemidi_token,
362                         {
363                                 "Initiator Token",
364                                 "applemidi.initiator_token",
365                                 FT_UINT32,
366                                 BASE_HEX,
367                                 NULL,
368                                 0x0,
369                                 NULL, HFILL
370                         }
371                 },
372                 {
373                         &hf_applemidi_ssrc,
374                         {
375                                 "Sender SSRC",
376                                 "applemidi.sender_ssrc",
377                                 FT_UINT32,
378                                 BASE_HEX,
379                                 NULL,
380                                 0x0,
381                                 NULL, HFILL
382                         }
383                 },
384                 {
385                         &hf_applemidi_name,
386                         {
387                                 "Name",
388                                 "applemidi.name",
389                                 FT_STRING,
390                                 BASE_NONE,
391                                 NULL,
392                                 0x0,
393                                 NULL, HFILL
394                         }
395                 },
396                 {
397                         &hf_applemidi_count,
398                         {
399                                 "Count",
400                                 "applemidi.count",
401                                 FT_UINT8,
402                                 BASE_DEC,
403                                 NULL,
404                                 0x0,
405                                 NULL, HFILL
406                         }
407                 },
408                 {
409                         &hf_applemidi_padding,
410                         {
411                                 "Padding",
412                                 "applemidi.padding",
413                                 FT_UINT24,
414                                 BASE_HEX,
415                                 NULL,
416                                 0x0,
417                                 NULL, HFILL
418                         }
419                 },
420                 {
421                         &hf_applemidi_timestamp1,
422                         {
423                                 "Timestamp 1",
424                                 "applemidi.timestamp1",
425                                 FT_UINT64,
426                                 BASE_HEX,
427                                 NULL,
428                                 0x0,
429                                 NULL, HFILL
430                         }
431                 },
432                 {
433                         &hf_applemidi_timestamp2,
434                         {
435                                 "Timestamp 2",
436                                 "applemidi.timestamp2",
437                                 FT_UINT64,
438                                 BASE_HEX,
439                                 NULL,
440                                 0x0,
441                                 NULL, HFILL
442                         }
443                 },
444                 {
445                         &hf_applemidi_timestamp3,
446                         {
447                                 "Timestamp 3",
448                                 "applemidi.timestamp3",
449                                 FT_UINT64,
450                                 BASE_HEX,
451                                 NULL,
452                                 0x0,
453                                 NULL, HFILL
454                         }
455                 },
456                 {
457                         &hf_applemidi_sequence_num,
458                         {
459                                 "Sequence Number",
460                                 "applemidi.sequence_number",
461                                 FT_UINT32,
462                                 BASE_HEX,
463                                 NULL,
464                                 0x0,
465                                 NULL, HFILL
466                         }
467                 },
468                 {
469                         &hf_applemidi_rtp_sequence_num,
470                         {
471                                 "RTP Sequence Number",
472                                 "applemidi.rtp_sequence_number",
473                                 FT_UINT16,
474                                 BASE_DEC,
475                                 NULL,
476                                 0x0,
477                                 NULL, HFILL
478                         }
479                 },
480                 {
481                         &hf_applemidi_rtp_bitrate_limit,
482                         {
483                                 "Bitrate limit",
484                                 "applemidi.bitrate_limit",
485                                 FT_UINT32,
486                                 BASE_DEC,
487                                 NULL,
488                                 0x0,
489                                 NULL, HFILL
490                         }
491                 },
492                 {
493                         &hf_applemidi_unknown_data,
494                         {
495                                 "Unknown Data",
496                                 "applemidi.unknown_data",
497                                 FT_BYTES,
498                                 BASE_NONE,
499                                 NULL,
500                                 0x00,
501                                 NULL, HFILL
502                         }
503                 },
504         };
505
506
507         static gint *ett[] = {
508                 &ett_applemidi,
509                 &ett_applemidi_seq_num
510         };
511
512         proto_applemidi = proto_register_protocol( APPLEMIDI_DISSECTOR_NAME,
513                                                    APPLEMIDI_DISSECTOR_SHORTNAME,
514                                                    APPLEMIDI_DISSECTOR_ABBREVIATION );
515         proto_register_field_array( proto_applemidi, hf, array_length( hf ) );
516         proto_register_subtree_array( ett, array_length( ett ) );
517
518 }
519
520 void
521 proto_reg_handoff_applemidi( void ) {
522
523
524         applemidi_handle = create_dissector_handle( dissect_applemidi, proto_applemidi );
525
526         /* If we cannot decode the data it will be RTP-MIDI since the Apple session protocol uses
527          * two ports: the control-port and the MIDI-port.  On both ports an invitation is being sent.
528          * The second port is then used for the RTP-MIDI-data. So if we can't find valid AppleMidi
529          * packets, it will be most likely RTP-MIDI...
530          */
531         rtp_handle = find_dissector( "rtp" );
532         heur_dissector_add( "udp", dissect_applemidi_heur, proto_applemidi );
533
534 }