2 * Routines for "Open Sound Control" packet dissection
3 * Copyright 2014-2016 Hanspeter Portner <dev@open-music-kontrollers.ch>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
13 * Specification 1.1 (http://opensoundcontrol.org/spec-1_1)
14 * - TCP dissection with: SLIP framing
15 * Specification 1.0 (http://opensoundcontrol.org/spec-1_0)
16 * - based on default argument types: i,f,s,b
17 * - including widely used extension types: T,F,N,I,h,d,t,S,c,r,m
18 * - TCP dissection with: int32 size prefix framing
20 * - Schmeder, A., Freed, A., and Wessel, D.,
21 * "Best practices for Open Sound Control",
22 * Linux Audio Conference, Utrecht, The Netherlands, 2010.
23 * - Freed, A., Schmeder, A.,
24 * "Features and Future of Open Sound Control version 1.1 for NIME",
25 * NIME Conference 2009.
26 * - Wright, M., Freed, A.,
27 * "Open Sound Control: A New Protocol for Communicating with Sound Synthesizers",
28 * International Computer Music Conference, Thessaloniki, Greece, 1997.
29 * - https://tools.ietf.org/html/rfc1055 (SLIP)
35 #include <epan/packet.h>
36 #include <epan/exceptions.h>
37 #include <epan/unit_strings.h>
39 #include <wsutil/array.h>
40 #include "packet-tcp.h"
42 void proto_register_osc(void);
43 void proto_reg_handoff_osc(void);
45 /* Open Sound Control (OSC) argument types enumeration */
46 typedef enum _OSC_Type
{
67 /* characters not allowed in OSC path string */
68 static const char invalid_path_chars
[] = {
73 /* allowed characters in OSC format string */
74 static const char valid_format_chars
[] = {
75 OSC_INT32
, OSC_FLOAT
, OSC_STRING
, OSC_BLOB
,
76 OSC_TRUE
, OSC_FALSE
, OSC_NIL
, OSC_BANG
,
77 OSC_INT64
, OSC_DOUBLE
, OSC_TIMETAG
,
78 OSC_SYMBOL
, OSC_CHAR
, OSC_RGBA
, OSC_MIDI
,
82 typedef enum _MIDI_Status_Type
{
83 MIDI_STATUS_NOTE_OFF
= 0x8,
84 MIDI_STATUS_NOTE_ON
= 0x9,
85 MIDI_STATUS_NOTE_PRESSURE
= 0xA,
86 MIDI_STATUS_CONTROLLER
= 0xB,
87 MIDI_STATUS_PROGRAM_CHANGE
= 0xC,
88 MIDI_STATUS_CHANNEL_PRESSURE
= 0xD,
89 MIDI_STATUS_PITCH_BENDER
= 0xE
92 /* Standard MIDI Message Type */
93 static const value_string MIDI_status
[] = {
94 { 0x0, "Invalid Message" },
95 { MIDI_STATUS_NOTE_OFF
, "Note Off" },
96 { MIDI_STATUS_NOTE_ON
, "Note On" },
97 { MIDI_STATUS_NOTE_PRESSURE
, "Note Pressure" },
98 { MIDI_STATUS_CONTROLLER
, "Controller" },
99 { MIDI_STATUS_PROGRAM_CHANGE
, "Program Change" },
100 { MIDI_STATUS_CHANNEL_PRESSURE
, "Channel Pressure" },
101 { MIDI_STATUS_PITCH_BENDER
, "Pitch Bender" },
105 static value_string_ext MIDI_status_ext
= VALUE_STRING_EXT_INIT(MIDI_status
);
107 /* Standard MIDI Message Type */
108 static const value_string MIDI_system
[] = {
109 { 0xF0, "System Exclusive Begin" },
110 { 0xF1, "MTC Quarter Frame" },
111 { 0xF2, "Song Position" },
112 { 0xF3, "Song Select" },
113 { 0xF6, "Tune Request" },
116 { 0xFB, "Continue" },
118 { 0xFE, "Active Sensing" },
123 static value_string_ext MIDI_system_ext
= VALUE_STRING_EXT_INIT(MIDI_system
);
125 /* Standard MIDI Note Numbers */
126 static const value_string MIDI_note
[] = {
127 { 0x00, "C-0" }, { 0x01, "#C-0" },
128 { 0x02, "D-0" }, { 0x03, "#D-0" },
130 { 0x05, "F-0" }, { 0x06, "#F-0" },
131 { 0x07, "G-0" }, { 0x08, "#G-0" },
132 { 0x09, "A-0" }, { 0x0A, "#A-0" },
135 { 0x0C, "C-1" }, { 0x0D, "#C-1" },
136 { 0x0E, "D-1" }, { 0x0F, "#D-1" },
138 { 0x11, "F-1" }, { 0x12, "#F-1" },
139 { 0x13, "G-1" }, { 0x14, "#G-1" },
140 { 0x15, "A-1" }, { 0x16, "#A-1" },
143 { 0x18, "C-2" }, { 0x19, "#C-2" },
144 { 0x1A, "D-2" }, { 0x1B, "#D-2" },
146 { 0x1D, "F-2" }, { 0x1E, "#F-2" },
147 { 0x1F, "G-2" }, { 0x20, "#G-2" },
148 { 0x21, "A-2" }, { 0x22, "#A-2" },
151 { 0x24, "C-3" }, { 0x25, "#C-3" },
152 { 0x26, "D-3" }, { 0x27, "#D-3" },
154 { 0x29, "F-3" }, { 0x2A, "#F-3" },
155 { 0x2B, "G-3" }, { 0x2C, "#G-3" },
156 { 0x2D, "A-3" }, { 0x2E, "#A-3" },
159 { 0x30, "C-4" }, { 0x31, "#C-4" },
160 { 0x32, "D-4" }, { 0x33, "#D-4" },
162 { 0x35, "F-4" }, { 0x36, "#F-4" },
163 { 0x37, "G-4" }, { 0x38, "#G-4" },
164 { 0x39, "A-4" }, { 0x3A, "#A-4" },
167 { 0x3C, "C-5" }, { 0x3D, "#C-5" },
168 { 0x3E, "D-5" }, { 0x3F, "#D-5" },
170 { 0x41, "F-5" }, { 0x42, "#F-5" },
171 { 0x43, "G-5" }, { 0x44, "#G-5" },
172 { 0x45, "A-5" }, { 0x46, "#A-5" },
175 { 0x48, "C-6" }, { 0x49, "#C-6" },
176 { 0x4A, "D-6" }, { 0x4B, "#D-6" },
178 { 0x4D, "F-6" }, { 0x4E, "#F-6" },
179 { 0x4F, "G-6" }, { 0x50, "#G-6" },
180 { 0x51, "A-6" }, { 0x52, "#A-6" },
183 { 0x54, "C-7" }, { 0x55, "#C-7" },
184 { 0x56, "D-7" }, { 0x57, "#D-7" },
186 { 0x59, "F-7" }, { 0x5A, "#F-7" },
187 { 0x5B, "G-7" }, { 0x5C, "#G-7" },
188 { 0x5D, "A-7" }, { 0x5E, "#A-7" },
191 { 0x60, "C-8" }, { 0x61, "#C-8" },
192 { 0x62, "D-8" }, { 0x63, "#D-8" },
194 { 0x65, "F-8" }, { 0x66, "#F-8" },
195 { 0x67, "G-8" }, { 0x68, "#G-8" },
196 { 0x69, "A-8" }, { 0x6A, "#A-8" },
199 { 0x6C, "C-9" }, { 0x6D, "#C-9" },
200 { 0x6E, "D-9" }, { 0x6F, "#D-9" },
202 { 0x71, "F-9" }, { 0x72, "#F-9" },
203 { 0x73, "G-9" }, { 0x74, "#G-9" },
204 { 0x75, "A-9" }, { 0x76, "#A-9" },
207 { 0x78, "C-10" }, { 0x79, "#C-10" },
208 { 0x7A, "D-10" }, { 0x7B, "#D-10" },
210 { 0x7D, "F-10" }, { 0x7E, "#F-10" },
215 static value_string_ext MIDI_note_ext
= VALUE_STRING_EXT_INIT(MIDI_note
);
217 /* Standard MIDI Controller Numbers */
218 static const value_string MIDI_control
[] = {
219 { 0x00, "Bank Selection" },
220 { 0x01, "Modulation" },
223 { 0x05, "Portamento Time" },
224 { 0x06, "Data Entry" },
225 { 0x07, "Main Volume" },
228 { 0x0B, "Expression" },
231 { 0x10, "General Purpose 1" },
232 { 0x11, "General Purpose 2" },
233 { 0x12, "General Purpose 3" },
234 { 0x13, "General Purpose 4" },
235 { 0x20, "Bank Selection" },
236 { 0x21, "Modulation" },
239 { 0x25, "Portamento Time" },
240 { 0x26, "Data Entry" },
241 { 0x27, "Main Volume" },
244 { 0x2B, "Expression" },
247 { 0x30, "General Purpose 1" },
248 { 0x31, "General Purpose 2" },
249 { 0x32, "General Purpose 3" },
250 { 0x33, "General Purpose 4" },
251 { 0x40, "Sustain Pedal" },
252 { 0x41, "Portamento" },
253 { 0x42, "Sostenuto" },
254 { 0x43, "Soft Pedal" },
255 { 0x44, "Legato Foot Switch" },
257 { 0x46, "SC1 Sound Variation" },
258 { 0x47, "SC2 Timbre" },
259 { 0x48, "SC3 Release Time" },
260 { 0x49, "SC4 Attack Time" },
261 { 0x4A, "SC5 Brightness" },
267 { 0x50, "General Purpose 5" },
268 { 0x51, "General Purpose 6" },
269 { 0x52, "General Purpose 7" },
270 { 0x53, "General Purpose 8" },
271 { 0x54, "Portamento Control" },
272 { 0x5B, "E1 Reverb Depth" },
273 { 0x5C, "E2 Tremolo Depth" },
274 { 0x5D, "E3 Chorus Depth" },
275 { 0x5E, "E4 Detune Depth" },
276 { 0x5F, "E5 Phaser Depth" },
277 { 0x60, "Data Increment" },
278 { 0x61, "Data Decrement" },
279 { 0x62, "Non-registered Parameter Number" },
280 { 0x63, "Non-registered Parameter Number" },
281 { 0x64, "Registered Parameter Number" },
282 { 0x65, "Registered Parameter Number" },
283 { 0x78, "All Sounds Off" },
284 { 0x79, "Reset Controllers" },
285 { 0x7A, "Local Control Switch" },
286 { 0x7B, "All Notes Off" },
287 { 0x7C, "Omni Off" },
294 static value_string_ext MIDI_control_ext
= VALUE_STRING_EXT_INIT(MIDI_control
);
296 static const char *immediate_fmt
= "%s";
297 static const char *immediate_str
= "Immediate";
298 static const char *bundle_str
= "#bundle";
300 /* Initialize the protocol and registered fields */
301 static dissector_handle_t osc_udp_handle
;
302 static dissector_handle_t osc_tcp_handle
;
304 static int proto_osc
;
306 static int hf_osc_bundle_type
;
307 static int hf_osc_message_type
;
308 static int hf_osc_message_header_type
;
309 static int hf_osc_message_blob_type
;
310 static int hf_osc_message_midi_type
;
311 static int hf_osc_message_rgba_type
;
313 static int hf_osc_bundle_timetag_type
;
314 static int hf_osc_bundle_element_size_type
;
316 static int hf_osc_message_path_type
;
317 static int hf_osc_message_format_type
;
319 static int hf_osc_message_int32_type
;
320 static int hf_osc_message_float_type
;
321 static int hf_osc_message_string_type
;
322 static int hf_osc_message_blob_size_type
;
323 static int hf_osc_message_blob_data_type
;
325 static int hf_osc_message_true_type
;
326 static int hf_osc_message_false_type
;
327 static int hf_osc_message_nil_type
;
328 static int hf_osc_message_bang_type
;
330 static int hf_osc_message_int64_type
;
331 static int hf_osc_message_double_type
;
332 static int hf_osc_message_timetag_type
;
334 static int hf_osc_message_symbol_type
;
335 static int hf_osc_message_char_type
;
337 static int hf_osc_message_rgba_red_type
;
338 static int hf_osc_message_rgba_green_type
;
339 static int hf_osc_message_rgba_blue_type
;
340 static int hf_osc_message_rgba_alpha_type
;
342 static int hf_osc_message_midi_port_type
;
343 static int hf_osc_message_midi_system_type
;
344 static int hf_osc_message_midi_channel_type
;
345 static int hf_osc_message_midi_status_type
;
346 static int hf_osc_message_midi_data1_type
;
347 static int hf_osc_message_midi_data2_type
;
348 static int hf_osc_message_midi_velocity_type
;
349 static int hf_osc_message_midi_pressure_type
;
350 static int hf_osc_message_midi_note_type
;
351 static int hf_osc_message_midi_controller_type
;
352 static int hf_osc_message_midi_bender_type
;
354 /* Initialize the subtree pointers */
355 static int ett_osc_packet
;
356 static int ett_osc_bundle
;
357 static int ett_osc_message
;
358 static int ett_osc_message_header
;
359 static int ett_osc_blob
;
360 static int ett_osc_rgba
;
361 static int ett_osc_midi
;
363 /* check for valid path string */
365 is_valid_path(const char *path
)
370 for(ptr
=path
+1; *ptr
!='\0'; ptr
++)
371 if(!g_ascii_isprint(*ptr
) || (strchr(invalid_path_chars
, *ptr
) != NULL
) )
376 /* check for valid format string */
378 is_valid_format(const char *format
)
383 for(ptr
=format
+1; *ptr
!='\0'; ptr
++)
384 if(strchr(valid_format_chars
, *ptr
) == NULL
)
389 /* Dissect OSC message */
391 dissect_osc_message(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_item
*ti
, proto_tree
*osc_tree
, int offset
, int len
)
393 proto_tree
*message_tree
;
394 proto_tree
*header_tree
;
397 int end
= offset
+ len
;
407 path_offset
= offset
;
408 path
= tvb_get_stringz_enc(pinfo
->pool
, tvb
, path_offset
, &path_len
, ENC_ASCII
);
409 if( (rem
= path_len
%4) ) path_len
+= 4-rem
;
411 if(!is_valid_path(path
))
415 format_offset
= path_offset
+ path_len
;
416 format
= tvb_get_stringz_enc(pinfo
->pool
, tvb
, format_offset
, &format_len
, ENC_ASCII
);
417 if( (rem
= format_len
%4) ) format_len
+= 4-rem
;
419 if(!is_valid_format(format
))
423 ti
= proto_tree_add_none_format(osc_tree
, hf_osc_message_type
, tvb
, offset
, len
, "Message: %s %s", path
, format
);
424 message_tree
= proto_item_add_subtree(ti
, ett_osc_message
);
427 ti
= proto_tree_add_item(message_tree
, hf_osc_message_header_type
, tvb
, offset
, path_len
+format_len
, ENC_NA
);
428 header_tree
= proto_item_add_subtree(ti
, ett_osc_message_header
);
431 proto_tree_add_item(header_tree
, hf_osc_message_path_type
, tvb
, path_offset
, path_len
, ENC_ASCII
| ENC_NA
);
434 proto_tree_add_item(header_tree
, hf_osc_message_format_type
, tvb
, format_offset
, format_len
, ENC_ASCII
| ENC_NA
);
436 offset
+= path_len
+ format_len
;
438 /* ::parse argument:: */
439 ptr
= format
+ 1; /* skip ',' */
440 while( (*ptr
!= '\0') && (offset
< end
) )
445 proto_tree_add_item(message_tree
, hf_osc_message_int32_type
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
449 proto_tree_add_item(message_tree
, hf_osc_message_float_type
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
453 slen
= tvb_strsize(tvb
, offset
);
454 if( (rem
= slen
%4) ) slen
+= 4-rem
;
455 proto_tree_add_item(message_tree
, hf_osc_message_string_type
, tvb
, offset
, slen
, ENC_ASCII
| ENC_NA
);
461 proto_tree
*blob_tree
;
463 int32_t blen
= tvb_get_ntohl(tvb
, offset
);
465 if( (rem
= slen
%4) ) slen
+= 4-rem
;
467 bi
= proto_tree_add_none_format(message_tree
, hf_osc_message_blob_type
, tvb
, offset
, 4+slen
, "Blob: %i bytes", blen
);
468 blob_tree
= proto_item_add_subtree(bi
, ett_osc_blob
);
470 proto_tree_add_int(blob_tree
, hf_osc_message_blob_size_type
, tvb
, offset
, 4, blen
);
473 /* check for zero length blob */
477 proto_tree_add_item(blob_tree
, hf_osc_message_blob_data_type
, tvb
, offset
, slen
, ENC_NA
);
483 proto_tree_add_item(message_tree
, hf_osc_message_true_type
, tvb
, offset
, 0, ENC_NA
);
486 proto_tree_add_item(message_tree
, hf_osc_message_false_type
, tvb
, offset
, 0, ENC_NA
);
489 proto_tree_add_item(message_tree
, hf_osc_message_nil_type
, tvb
, offset
, 0, ENC_NA
);
492 proto_tree_add_item(message_tree
, hf_osc_message_bang_type
, tvb
, offset
, 0, ENC_NA
);
496 proto_tree_add_item(message_tree
, hf_osc_message_int64_type
, tvb
, offset
, 8, ENC_BIG_ENDIAN
);
500 proto_tree_add_item(message_tree
, hf_osc_message_double_type
, tvb
, offset
, 8, ENC_BIG_ENDIAN
);
505 uint32_t sec
= tvb_get_ntohl(tvb
, offset
);
506 uint32_t frac
= tvb_get_ntohl(tvb
, offset
+4);
508 if( (sec
== 0) && (frac
== 1) )
509 proto_tree_add_time_format_value(message_tree
, hf_osc_message_timetag_type
, tvb
, offset
, 8, &ns
, immediate_fmt
, immediate_str
);
511 proto_tree_add_item(message_tree
, hf_osc_message_timetag_type
, tvb
, offset
, 8, ENC_TIME_NTP
| ENC_BIG_ENDIAN
);
517 slen
= tvb_strsize(tvb
, offset
);
518 if( (rem
= slen
%4) ) slen
+= 4-rem
;
519 proto_tree_add_item(message_tree
, hf_osc_message_symbol_type
, tvb
, offset
, slen
, ENC_ASCII
| ENC_NA
);
524 proto_tree_add_item(message_tree
, hf_osc_message_char_type
, tvb
, offset
, 1, ENC_ASCII
| ENC_NA
);
530 proto_tree
*rgba_tree
;
532 ri
= proto_tree_add_item(message_tree
, hf_osc_message_rgba_type
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
533 rgba_tree
= proto_item_add_subtree(ri
, ett_osc_rgba
);
535 proto_tree_add_item(rgba_tree
, hf_osc_message_rgba_red_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
537 proto_tree_add_item(rgba_tree
, hf_osc_message_rgba_green_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
539 proto_tree_add_item(rgba_tree
, hf_osc_message_rgba_blue_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
541 proto_tree_add_item(rgba_tree
, hf_osc_message_rgba_alpha_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
547 const char *status_str
;
548 proto_item
*mi
= NULL
;
549 proto_tree
*midi_tree
;
557 uint8_t status_shifted
;
559 port
= tvb_get_uint8(tvb
, offset
);
560 command
= tvb_get_uint8(tvb
, offset
+1);
561 data1
= tvb_get_uint8(tvb
, offset
+2);
562 data2
= tvb_get_uint8(tvb
, offset
+3);
564 status
= command
& 0xF0;
565 channel
= command
& 0x0F;
567 system_msg
= status
== 0xF0; /* is system message */
568 status_shifted
= status
>> 4;
571 status_str
= val_to_str_ext_const(command
, &MIDI_system_ext
, "Unknown");
573 status_str
= val_to_str_ext_const(status_shifted
, &MIDI_status_ext
, "Unknown");
577 mi
= proto_tree_add_none_format(message_tree
, hf_osc_message_midi_type
, tvb
, offset
, 4,
578 "MIDI: Port %i, %s, %i, %i",
579 port
, status_str
, data1
, data2
);
583 switch(status_shifted
)
585 case MIDI_STATUS_NOTE_ON
:
586 case MIDI_STATUS_NOTE_OFF
:
587 case MIDI_STATUS_NOTE_PRESSURE
:
589 const char *note_str
;
590 note_str
= val_to_str_ext_const(data1
, &MIDI_note_ext
, "Unknown");
592 mi
= proto_tree_add_none_format(message_tree
, hf_osc_message_midi_type
, tvb
, offset
, 4,
593 "MIDI: Port %i, Channel %i, %s, %s, %i",
594 port
, channel
, status_str
, note_str
, data2
);
597 case MIDI_STATUS_CONTROLLER
:
599 const char *control_str
;
600 control_str
= val_to_str_ext_const(data1
, &MIDI_control_ext
, "Unknown");
602 mi
= proto_tree_add_none_format(message_tree
, hf_osc_message_midi_type
, tvb
, offset
, 4,
603 "MIDI: Port %i, Channel %i, %s, %s, %i",
604 port
, channel
, status_str
, control_str
, data2
);
607 case MIDI_STATUS_PITCH_BENDER
:
609 const int bender
= (((int)data2
<< 7) | (int)data1
) - 0x2000;
611 mi
= proto_tree_add_none_format(message_tree
, hf_osc_message_midi_type
, tvb
, offset
, 4,
612 "MIDI: Port %i, Channel %i, %s, %i",
613 port
, channel
, status_str
, bender
);
618 mi
= proto_tree_add_none_format(message_tree
, hf_osc_message_midi_type
, tvb
, offset
, 4,
619 "MIDI: Port %i, Channel %i, %s, %i, %i",
620 port
, channel
, status_str
, data1
, data2
);
625 midi_tree
= proto_item_add_subtree(mi
, ett_osc_midi
);
627 proto_tree_add_item(midi_tree
, hf_osc_message_midi_port_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
632 proto_tree_add_item(midi_tree
, hf_osc_message_midi_system_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
635 proto_tree_add_item(midi_tree
, hf_osc_message_midi_data1_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
638 proto_tree_add_item(midi_tree
, hf_osc_message_midi_data2_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
643 proto_tree_add_item(midi_tree
, hf_osc_message_midi_status_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
644 proto_tree_add_item(midi_tree
, hf_osc_message_midi_channel_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
647 switch(status_shifted
)
649 case MIDI_STATUS_NOTE_ON
:
650 case MIDI_STATUS_NOTE_OFF
:
652 proto_tree_add_item(midi_tree
, hf_osc_message_midi_note_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
655 proto_tree_add_item(midi_tree
, hf_osc_message_midi_velocity_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
660 case MIDI_STATUS_NOTE_PRESSURE
:
662 proto_tree_add_item(midi_tree
, hf_osc_message_midi_note_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
665 proto_tree_add_item(midi_tree
, hf_osc_message_midi_pressure_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
670 case MIDI_STATUS_CONTROLLER
:
672 proto_tree_add_item(midi_tree
, hf_osc_message_midi_controller_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
675 proto_tree_add_item(midi_tree
, hf_osc_message_midi_data2_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
680 case MIDI_STATUS_CHANNEL_PRESSURE
:
682 proto_tree_add_item(midi_tree
, hf_osc_message_midi_pressure_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
685 proto_tree_add_item(midi_tree
, hf_osc_message_midi_data2_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
690 case MIDI_STATUS_PITCH_BENDER
:
692 const int bender
= (((int)data2
<< 7) | (int)data1
) - 0x2000;
694 proto_tree_add_int(midi_tree
, hf_osc_message_midi_bender_type
, tvb
, offset
, 2, bender
);
701 proto_tree_add_item(midi_tree
, hf_osc_message_midi_data1_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
704 proto_tree_add_item(midi_tree
, hf_osc_message_midi_data2_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
716 /* if we get here, there must be a bug in the dissector */
717 DISSECTOR_ASSERT_NOT_REACHED();
729 /* Dissect OSC bundle */
731 // NOLINTNEXTLINE(misc-no-recursion)
732 dissect_osc_bundle(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_item
*ti
, proto_tree
*osc_tree
, int offset
, int len
)
734 proto_tree
*bundle_tree
;
735 int end
= offset
+ len
;
740 /* check for valid #bundle */
741 if(tvb_strneql(tvb
, offset
, bundle_str
, 8) != 0)
745 ti
= proto_tree_add_item(osc_tree
, hf_osc_bundle_type
, tvb
, offset
, len
, ENC_NA
);
747 bundle_tree
= proto_item_add_subtree(ti
, ett_osc_bundle
);
749 offset
+= 8; /* skip bundle_str */
752 sec
= tvb_get_ntohl(tvb
, offset
);
753 frac
= tvb_get_ntohl(tvb
, offset
+4);
754 if( (sec
== 0) && (frac
== 1) )
755 proto_tree_add_time_format_value(bundle_tree
, hf_osc_bundle_timetag_type
, tvb
, offset
, 8, &ns
, immediate_fmt
, immediate_str
);
757 proto_tree_add_item(bundle_tree
, hf_osc_bundle_timetag_type
, tvb
, offset
, 8, ENC_TIME_NTP
| ENC_BIG_ENDIAN
);
760 /* ::read size, read block:: */
763 /* peek bundle element size */
766 /* read bundle element size */
767 proto_tree_add_item_ret_int(bundle_tree
, hf_osc_bundle_element_size_type
, tvb
, offset
, 4, ENC_BIG_ENDIAN
, &size
);
770 /* check for zero size bundle element */
774 /* peek first bundle element char */
775 increment_dissection_depth(pinfo
);
776 switch(tvb_get_uint8(tvb
, offset
))
778 case '#': /* this is a bundle */
779 if(dissect_osc_bundle(tvb
, pinfo
, ti
, bundle_tree
, offset
, size
))
783 case '/': /* this is a message */
784 if(dissect_osc_message(tvb
, pinfo
, ti
, bundle_tree
, offset
, size
))
789 return -1; /* neither message nor bundle */
791 decrement_dissection_depth(pinfo
);
793 /* check for integer overflow */
794 if(size
> INT_MAX
- offset
)
806 /* Dissect OSC PDU */
808 dissect_osc_pdu_common(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
, int offset
, int len
)
810 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "OSC");
811 col_clear(pinfo
->cinfo
, COL_INFO
);
813 if(tree
) /* we are being asked for details */
816 proto_tree
*osc_tree
;
818 /* create OSC packet */
819 ti
= proto_tree_add_item(tree
, proto_osc
, tvb
, 0, -1, ENC_NA
);
820 osc_tree
= proto_item_add_subtree(ti
, ett_osc_packet
);
822 /* peek first bundle element char */
823 switch(tvb_get_uint8(tvb
, offset
))
825 case '#': /* this is a bundle */
826 if(dissect_osc_bundle(tvb
, pinfo
, ti
, osc_tree
, offset
, len
))
830 case '/': /* this is a message */
831 if(dissect_osc_message(tvb
, pinfo
, ti
, osc_tree
, offset
, len
))
835 default: /* neither message nor bundle */
841 /* OSC TCP (OSC-1.0) */
844 get_osc_pdu_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
, void *data _U_
)
846 return tvb_get_ntohl(tvb
, offset
) + 4;
850 dissect_osc_tcp_pdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
854 pdu_len
= tvb_get_ntohl(tvb
, 0);
855 dissect_osc_pdu_common(tvb
, pinfo
, tree
, data
, 4, pdu_len
);
860 dissect_osc_tcp_1_0(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
862 tcp_dissect_pdus(tvb
, pinfo
, tree
, true, 4, get_osc_pdu_len
,
863 dissect_osc_tcp_pdu
, data
);
864 return tvb_reported_length(tvb
);
867 /* OSC TCP (OSC-1.1) */
868 #define SLIP_END 0xC0 /* 300 (octal), 192 (decimal), indicates end of packet */
869 #define SLIP_ESC 0xDB /* 333 (octal), 219 (decimal), indicates byte stuffing */
870 #define SLIP_END_REPLACE 0xDC /* 334 (octal), 220 (decimal), ESC ESC_END means END data byte */
871 #define SLIP_ESC_REPLACE 0xDD /* 335 (octal), 221 (decimal), ESC ESC_ESC means ESC data byte */
874 slip_decoded_len(const uint8_t *src
, unsigned available
)
877 const uint8_t *end
= src
+ available
;
879 bool escaped
= false;
881 for(ptr
= src
; ptr
< end
; ptr
++)
887 case SLIP_END_REPLACE
:
889 case SLIP_ESC_REPLACE
:
894 return -1; /* decode failed */
913 return -1; /* decode failed */
917 slip_decode(uint8_t *dst
, const uint8_t *src
, unsigned available
)
921 const uint8_t *end
= src
+ available
;
923 for(ptr
= src
; ptr
< end
; ptr
++)
931 case SLIP_END_REPLACE
:
934 case SLIP_ESC_REPLACE
:
945 dissect_osc_tcp_1_1(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
949 while(offset
< tvb_reported_length(tvb
))
951 const int available
= tvb_reported_length_remaining(tvb
, offset
);
952 const uint8_t *encoded_buf
= tvb_get_ptr(tvb
, offset
, -1);
953 const uint8_t *slip_end_found
= (const uint8_t *)memchr(encoded_buf
, SLIP_END
, available
);
954 unsigned encoded_len
;
956 uint8_t *decoded_buf
;
959 if(!slip_end_found
) /* no SLIP'd stream ending in this chunk */
961 /* we ran out of data: ask for more */
962 pinfo
->desegment_offset
= offset
;
963 pinfo
->desegment_len
= DESEGMENT_ONE_MORE_SEGMENT
;
964 return (offset
+ available
);
967 encoded_len
= (unsigned)(slip_end_found
+ 1 - encoded_buf
);
968 if(encoded_len
> 1) /* we have a non-empty SLIP'd stream*/
970 decoded_len
= slip_decoded_len(encoded_buf
, encoded_len
);
971 if(decoded_len
!= -1) /* is a valid SLIP'd stream */
973 decoded_buf
= (uint8_t *)wmem_alloc(pinfo
->pool
, decoded_len
);
975 slip_decode(decoded_buf
, encoded_buf
, encoded_len
);
977 next_tvb
= tvb_new_child_real_data(tvb
, decoded_buf
, decoded_len
, decoded_len
);
979 add_new_data_source(pinfo
, next_tvb
, "SLIP-decoded Data");
980 dissect_osc_pdu_common(next_tvb
, pinfo
, tree
, data
, 0, decoded_len
);
984 return 0; /* failed to decode SLIP'd stream */
988 offset
+= encoded_len
;
991 /* end of the tvb coincided with the end of a SLIP'd stream */
992 return tvb_captured_length(tvb
);
995 /* OSC TCP (OSC-1.0, OSC-1.1 fork) */
998 dissect_osc_tcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
1001 * int32 size prefix framing (OSC-1.0)
1002 * ... {(int32 size)(OSC packet)} ... {(int32 size)(OSC packet)} ...
1006 * SLIP framing (OSC-1.1)
1007 * ... {SLIP'd(OSC packet)} ... {SLIP'd(OSC)} ...
1010 const uint8_t first_byte
= tvb_get_uint8(tvb
, 0);
1011 const bool slip_encoded
= (first_byte
== SLIP_END
) /* empty SLIP frame*/
1012 || (first_byte
== '/') /* SLIP'd OSC message frame */
1013 || (first_byte
== '#'); /* SLIP'd OSC bundle frame */
1017 /* assume SLIP framing (OSC-1.1) */
1018 return dissect_osc_tcp_1_1(tvb
, pinfo
, tree
, data
);
1021 /* assume int32 size-prefixed framing (OSC-1.0) */
1022 return dissect_osc_tcp_1_0(tvb
, pinfo
, tree
, data
);
1028 dissect_osc_udp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
1032 pdu_len
= tvb_reported_length(tvb
);
1033 dissect_osc_pdu_common(tvb
,pinfo
, tree
, data
, 0, pdu_len
);
1039 dissect_osc_heur_udp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
1041 conversation_t
*conversation
;
1043 if(tvb_captured_length(tvb
) < 8)
1046 /* peek first string */
1047 if(tvb_strneql(tvb
, 0, bundle_str
, 8) != 0) /* no OSC bundle */
1052 volatile bool valid
= false;
1054 /* Check for valid path */
1055 /* Don't propagate any exceptions upwards during heuristics check */
1056 /* XXX: this check is a bit expensive; Consider: use UDP port pref ? */
1058 slen
= tvb_strsize(tvb
, offset
);
1059 if(is_valid_path(tvb_get_ptr(tvb
, offset
, slen
))) {
1062 if( (rem
= slen
%4) ) slen
+= 4-rem
;
1065 /* peek next string */
1066 slen
= tvb_strsize(tvb
, offset
);
1068 /* check for valid format */
1069 if(is_valid_format(tvb_get_ptr(tvb
, offset
, slen
)))
1082 /* if we get here, then it's an Open Sound Control packet (bundle or message) */
1084 /* specify that dissect_osc is to be called directly from now on for packets for this connection */
1085 conversation
= find_or_create_conversation(pinfo
);
1086 conversation_set_dissector(conversation
, osc_udp_handle
);
1088 /* do the dissection */
1089 dissect_osc_udp(tvb
, pinfo
, tree
, data
);
1091 return true; /* OSC heuristics was matched */
1094 /* Register the protocol with Wireshark */
1096 proto_register_osc(void)
1098 static hf_register_info hf
[] = {
1099 { &hf_osc_bundle_type
, { "Bundle", "osc.bundle",
1102 "Bundle structure", HFILL
} },
1103 { &hf_osc_bundle_timetag_type
, { "Timetag", "osc.bundle.timetag",
1104 FT_ABSOLUTE_TIME
, ABSOLUTE_TIME_UTC
,
1106 "Scheduled bundle execution time", HFILL
} },
1108 { &hf_osc_bundle_element_size_type
, { "Size", "osc.bundle.element.size",
1109 FT_INT32
, BASE_DEC
|BASE_UNIT_STRING
,
1110 UNS(&units_byte_bytes
), 0x0,
1111 "Bundle element size", HFILL
} },
1113 { &hf_osc_message_type
, { "Message", "osc.message",
1116 "Message structure", HFILL
} },
1117 { &hf_osc_message_header_type
, { "Header", "osc.message.header",
1120 "Message header", HFILL
} },
1121 { &hf_osc_message_path_type
, { "Path", "osc.message.header.path",
1122 FT_STRING
, BASE_NONE
,
1124 "Message path", HFILL
} },
1125 { &hf_osc_message_format_type
, { "Format", "osc.message.header.format",
1126 FT_STRING
, BASE_NONE
,
1128 "Message format", HFILL
} },
1130 { &hf_osc_message_int32_type
, { "Int32", "osc.message.int32",
1133 "32bit integer value", HFILL
} },
1134 { &hf_osc_message_float_type
, { "Float", "osc.message.float",
1135 FT_FLOAT
, BASE_NONE
,
1137 "Floating point value", HFILL
} },
1138 { &hf_osc_message_string_type
, { "String", "osc.message.string",
1139 FT_STRING
, BASE_NONE
,
1141 "String value", HFILL
} },
1143 { &hf_osc_message_blob_type
, { "Blob", "osc.message.blob",
1146 "Binary blob value", HFILL
} },
1147 { &hf_osc_message_blob_size_type
, { "Size", "osc.message.blob.size",
1148 FT_INT32
, BASE_DEC
|BASE_UNIT_STRING
,
1149 UNS(&units_byte_bytes
), 0x0,
1150 "Binary blob size", HFILL
} },
1151 { &hf_osc_message_blob_data_type
, { "Data", "osc.message.blob.data",
1152 FT_BYTES
, BASE_NONE
,
1154 "Binary blob data", HFILL
} },
1156 { &hf_osc_message_true_type
, { "True", "osc.message.true",
1159 "Boolean true value", HFILL
} },
1160 { &hf_osc_message_false_type
, { "False", "osc.message.false",
1163 "Boolean false value", HFILL
} },
1164 { &hf_osc_message_nil_type
, { "Nil", "osc.message.nil",
1167 "Nil value", HFILL
} },
1168 { &hf_osc_message_bang_type
, { "Bang", "osc.message.bang",
1171 "Infinity, Impulse or Bang value", HFILL
} },
1173 { &hf_osc_message_int64_type
, { "Int64", "osc.message.int64",
1176 "64bit integer value", HFILL
} },
1177 { &hf_osc_message_double_type
, { "Double", "osc.message.double",
1178 FT_DOUBLE
, BASE_NONE
,
1180 "Double value", HFILL
} },
1181 { &hf_osc_message_timetag_type
, { "Timetag", "osc.message.timetag",
1182 FT_ABSOLUTE_TIME
, ABSOLUTE_TIME_UTC
,
1184 "NTP time value", HFILL
} },
1186 { &hf_osc_message_symbol_type
, { "Symbol", "osc.message.symbol",
1187 FT_STRING
, BASE_NONE
,
1189 "Symbol value", HFILL
} },
1190 { &hf_osc_message_char_type
, { "Char", "osc.message.char",
1191 FT_STRING
, BASE_NONE
,
1193 "Character value", HFILL
} },
1195 { &hf_osc_message_rgba_type
, { "RGBA", "osc.message.rgba",
1196 FT_UINT32
, BASE_HEX
,
1198 "RGBA color value", HFILL
} },
1199 { &hf_osc_message_rgba_red_type
, { "Red", "osc.message.rgba.red",
1202 "Red color component", HFILL
} },
1203 { &hf_osc_message_rgba_green_type
, { "Green", "osc.message.rgba.green",
1206 "Green color component", HFILL
} },
1207 { &hf_osc_message_rgba_blue_type
, { "Blue", "osc.message.rgba.blue",
1210 "Blue color component", HFILL
} },
1211 { &hf_osc_message_rgba_alpha_type
, { "Alpha", "osc.message.rgba.alpha",
1214 "Alpha transparency component", HFILL
} },
1216 { &hf_osc_message_midi_type
, { "MIDI", "osc.message.midi",
1219 "MIDI value", HFILL
} },
1220 { &hf_osc_message_midi_port_type
, { "Port", "osc.message.midi.port",
1223 "MIDI port", HFILL
} },
1224 { &hf_osc_message_midi_system_type
, { "System", "osc.message.midi.system",
1225 FT_UINT8
, BASE_HEX
| BASE_EXT_STRING
,
1226 &MIDI_system_ext
, 0x0,
1227 "MIDI system", HFILL
} },
1228 { &hf_osc_message_midi_status_type
, { "Status", "osc.message.midi.status",
1229 FT_UINT8
, BASE_HEX
| BASE_EXT_STRING
,
1230 &MIDI_status_ext
, 0xF0,
1231 "MIDI status", HFILL
} },
1232 { &hf_osc_message_midi_channel_type
, { "Channel", "osc.message.midi.channel",
1235 "MIDI channel", HFILL
} },
1236 { &hf_osc_message_midi_data1_type
, { "Data1", "osc.message.midi.data1",
1239 "MIDI data 1", HFILL
} },
1240 { &hf_osc_message_midi_data2_type
, { "Data2", "osc.message.midi.data2",
1243 "MIDI data 2", HFILL
} },
1244 { &hf_osc_message_midi_velocity_type
, { "Velocity", "osc.message.midi.velocity",
1247 "MIDI note velocity", HFILL
} },
1248 { &hf_osc_message_midi_pressure_type
, { "Pressure", "osc.message.midi.pressure",
1251 "MIDI note/channel pressure", HFILL
} },
1252 { &hf_osc_message_midi_note_type
, { "Note", "osc.message.midi.note",
1253 FT_UINT8
, BASE_DEC
| BASE_EXT_STRING
,
1254 &MIDI_note_ext
, 0x7F,
1255 "MIDI note", HFILL
} },
1256 { &hf_osc_message_midi_controller_type
, { "Controller", "osc.message.midi.controller",
1257 FT_UINT8
, BASE_DEC
| BASE_EXT_STRING
,
1258 &MIDI_control_ext
, 0x7F,
1259 "MIDI controller", HFILL
} },
1260 { &hf_osc_message_midi_bender_type
, { "Bender", "osc.message.midi.bender",
1263 "MIDI bender", HFILL
} }
1266 /* Setup protocol subtree array */
1267 static int *ett
[] = {
1271 &ett_osc_message_header
,
1277 proto_osc
= proto_register_protocol("Open Sound Control Encoding", "OSC", "osc");
1279 proto_register_field_array(proto_osc
, hf
, array_length(hf
));
1280 proto_register_subtree_array(ett
, array_length(ett
));
1282 osc_tcp_handle
= register_dissector("osc.tcp", dissect_osc_tcp
, proto_osc
);
1283 osc_udp_handle
= register_dissector("osc.udp", dissect_osc_udp
, proto_osc
);
1287 proto_reg_handoff_osc(void)
1289 /* XXX: Add port pref and "decode as" for UDP ? */
1290 /* (The UDP heuristic is a bit expensive */
1291 /* register as heuristic dissector for UDP connections */
1292 heur_dissector_add("udp", dissect_osc_heur_udp
, "Open Sound Control over UDP", "osc_udp", proto_osc
, HEURISTIC_DISABLE
);
1294 dissector_add_for_decode_as_with_preference("tcp.port", osc_tcp_handle
);
1298 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1303 * indent-tabs-mode: nil
1306 * vi: set shiftwidth=4 tabstop=8 expandtab:
1307 * :indentSize=4:tabSize=8:noTabs=true: