Skip to content

Commit 877b39f

Browse files
authored
Merge pull request #129 from lathoub/mallformed_packets
Fix for Malformed packets and long session names Thanks @folkertvanheusden and @hallvardkristiansen for reporting and fixing
2 parents ee56ae8 + be0be95 commit 877b39f

File tree

6 files changed

+131
-82
lines changed

6 files changed

+131
-82
lines changed

src/AppleMIDI.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ void AppleMIDISession<UdpClass, Settings, Platform>::parseControlPackets()
3838
#endif
3939
controlBuffer.pop_front();
4040
}
41+
else if (retVal == parserReturn::SessionNameVeryLong)
42+
{
43+
// purge the rest of the data in controlPort
44+
while (controlPort.read() >= 0) {}
45+
}
4146
}
4247
}
4348

src/AppleMIDI_Defs.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ enum parserReturn: uint8_t
3131
Processed,
3232
NotSureGiveMeMoreData,
3333
NotEnoughData,
34-
UnexpectedData,
34+
UnexpectedData,
35+
UnexpectedMidiData,
36+
UnexpectedJournalData,
37+
SessionNameVeryLong,
3538
};
3639

3740
#if defined(__AVR__)

src/AppleMIDI_Parser.h

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -78,29 +78,35 @@ class AppleMIDIParser
7878

7979
#ifdef KEEP_SESSION_NAME
8080
uint16_t bi = 0;
81-
while ((i < buffer.size()) && (buffer[i] != 0x00))
81+
while (i < buffer.size())
8282
{
8383
if (bi < DefaultSettings::MaxSessionNameLen)
84-
invitation.sessionName[bi++] = buffer[i];
85-
i++;
84+
invitation.sessionName[bi++] = buffer[i++];
85+
else
86+
i++;
8687
}
8788
invitation.sessionName[bi++] = '\0';
8889
#else
89-
while ((i < buffer.size()) && (buffer[i] != 0x00))
90+
while (i < buffer.size())
9091
i++;
9192
#endif
92-
// session name is optional.
93-
// If i > minimum size (16), then a sessionName was provided and must include 0x00
93+
auto retVal = parserReturn::Processed;
94+
95+
// when given a Session Name and the buffer has been fully processed and the
96+
// last character is not 'endl', then we got a very long sessionName. It will
97+
// continue in the next memory chunk of the packet. We don't care, so indicated
98+
// flush the remainder of the packet.
99+
// First part if the session name is kept, processing continues
94100
if (i > minimumLen)
95-
if (i == buffer.size() || buffer[i++] != 0x00)
96-
return parserReturn::NotEnoughData;
101+
if (i == buffer.size() && buffer[i] != 0x00)
102+
retVal = parserReturn::SessionNameVeryLong;
97103

98104
while (i--)
99105
buffer.pop_front(); // consume all the bytes that made up this message
100106

101107
session->ReceivedInvitation(invitation, portType);
102108

103-
return parserReturn::Processed;
109+
return retVal;
104110
}
105111
else if (0 == memcmp(command, amEndSession, sizeof(amEndSession)))
106112
{
@@ -267,29 +273,36 @@ class AppleMIDIParser
267273

268274
#ifdef KEEP_SESSION_NAME
269275
uint16_t bi = 0;
270-
while ((i < buffer.size()) && (buffer[i] != 0x00))
276+
while (i < buffer.size())
271277
{
272278
if (bi < DefaultSettings::MaxSessionNameLen)
273-
invitationAccepted.sessionName[bi++] = buffer[i];
274-
i++;
279+
invitationAccepted.sessionName[bi++] = buffer[i++];
280+
else
281+
i++;
275282
}
276283
invitationAccepted.sessionName[bi++] = '\0';
277284
#else
278-
while ((i < buffer.size()) && (buffer[i] != 0x00))
285+
while (i < buffer.size())
279286
i++;
280287
#endif
281-
// session name is optional.
282-
// If i > minimum size (16), then a sessionName was provided and must include 0x00
288+
289+
auto retVal = parserReturn::Processed;
290+
291+
// when given a Session Name and the buffer has been fully processed and the
292+
// last character is not 'endl', then we got a very long sessionName. It will
293+
// continue in the next memory chunk of the packet. We don't care, so indicated
294+
// flush the remainder of the packet.
295+
// First part if the session name is kept, processing continues
283296
if (i > minimumLen)
284-
if (i == buffer.size() || buffer[i++] != 0x00)
285-
return parserReturn::NotEnoughData;
297+
if (i == buffer.size() && buffer[i] != 0x00)
298+
retVal = parserReturn::SessionNameVeryLong;
286299

287300
while (i--)
288301
buffer.pop_front(); // consume all the bytes that made up this message
289302

290303
session->ReceivedInvitationAccepted(invitationAccepted, portType);
291304

292-
return parserReturn::Processed;
305+
return retVal;
293306
}
294307
else if (0 == memcmp(command, amInvitationRejected, sizeof(amInvitationRejected)))
295308
{

src/rtpMIDI_Parser.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class rtpMIDIParser
116116
if (buffer.size() < minimumLen)
117117
return parserReturn::NotSureGiveMeMoreData;
118118

119+
// 2.2. MIDI Payload (https://www.ietf.org/rfc/rfc4695.html#section-2.2)
119120
// The payload MUST begin with the MIDI command section. The
120121
// MIDI command section codes a (possibly empty) list of timestamped
121122
// MIDI commands and provides the essential service of the payload
@@ -153,8 +154,15 @@ class rtpMIDIParser
153154
if (midiCommandLength > 0)
154155
{
155156
auto retVal = decodeMidiSection(buffer);
156-
if (retVal != parserReturn::Processed)
157+
switch (retVal) {
158+
case parserReturn::Processed:
159+
break;
160+
case parserReturn::UnexpectedMidiData:
161+
// already processed MIDI data will be played
162+
_rtpHeadersComplete = false;
163+
default:
157164
return retVal;
165+
}
158166
}
159167

160168
// The payload MAY also contain a journal section. The journal section
@@ -165,8 +173,14 @@ class rtpMIDIParser
165173
if (rtpMidi_Flags & RTP_MIDI_CS_FLAG_J)
166174
{
167175
auto retVal = decodeJournalSection(buffer);
168-
if (retVal != parserReturn::Processed)
176+
switch (retVal) {
177+
case parserReturn::Processed:
178+
break;
179+
case parserReturn::UnexpectedJournalData:
180+
_rtpHeadersComplete = false;
181+
default:
169182
return retVal;
183+
}
170184
}
171185

172186
_rtpHeadersComplete = false;

src/rtpMIDI_Parser_MidiCommandSection.hpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,45 @@
1+
// https://www.ietf.org/rfc/rfc4695.html#section-3
2+
13
parserReturn decodeMidiSection(RtpBuffer_t &buffer)
24
{
35
int cmdCount = 0;
46

7+
// https://www.ietf.org/rfc/rfc4695.html#section-3.2
8+
//
9+
// The first MIDI channel command in the MIDI list MUST include a status
10+
// octet.Running status coding, as defined in[MIDI], MAY be used for
11+
// all subsequent MIDI channel commands in the list.As in[MIDI],
12+
// System Commonand System Exclusive messages(0xF0 ... 0xF7) cancel
13+
// the running status state, but System Real - time messages(0xF8 ...
14+
// 0xFF) do not affect the running status state. All System commands in
15+
// the MIDI list MUST include a status octet.
16+
17+
// As we note above, the first channel command in the MIDI list MUST
18+
// include a status octet.However, the corresponding command in the
19+
// original MIDI source data stream might not have a status octet(in
20+
// this case, the source would be coding the command using running
21+
// status). If the status octet of the first channel command in the
22+
// MIDI list does not appear in the source data stream, the P(phantom)
23+
// header bit MUST be set to 1. In all other cases, the P bit MUST be
24+
// set to 0.
25+
//
26+
// Note that the P bit describes the MIDI source data stream, not the
27+
// MIDI list encoding; regardless of the state of the P bit, the MIDI
28+
// list MUST include the status octet.
29+
//
30+
// As receivers MUST be able to decode running status, sender
31+
// implementors should feel free to use running status to improve
32+
// bandwidth efficiency. However, senders SHOULD NOT introduce timing
33+
// jitter into an existing MIDI command stream through an inappropriate
34+
// use or removal of running status coding. This warning primarily
35+
// applies to senders whose RTP MIDI streams may be transcoded onto a
36+
// MIDI 1.0 DIN cable[MIDI] by the receiver : both the timestamps and
37+
// the command coding (running status or not) must comply with the
38+
// physical restrictions of implicit time coding over a slow serial
39+
// line.
40+
41+
// (lathoub: RTP_MIDI_CS_FLAG_P((phantom) not implemented
42+
543
uint8_t runningstatus = 0;
644

745
/* Multiple MIDI-commands might follow - the exact number can only be discovered by really decoding the commands! */
@@ -34,6 +72,11 @@ parserReturn decodeMidiSection(RtpBuffer_t &buffer)
3472
return parserReturn::NotEnoughData;
3573
}
3674

75+
if (consumed > midiCommandLength) {
76+
buffer.clear();
77+
return parserReturn::UnexpectedMidiData;
78+
}
79+
3780
midiCommandLength -= consumed;
3881

3982
while (consumed--)

test/Ethernet.h

Lines changed: 32 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ class EthernetUDP
1515
EthernetUDP()
1616
{
1717
_port = 0;
18+
19+
20+
21+
1822
}
1923

2024
void begin(uint16_t port)
@@ -24,6 +28,21 @@ class EthernetUDP
2428
if (port == DEFAULT_CONTROL_PORT && true)
2529
{
2630
// AppleMIDI messages
31+
byte okSessionName[] = {
32+
0xff, 0xff, 0x49, 0x4e, 0x00, 0x00, 0x00, 0x02, 0x4e, 0x27, 0x95, 0x9e, 0x00, 0x00, 0xec, 0xf9,
33+
0x6c, 0x61, 0x70, 0x70, 0x69, 0x65, 0x6d, 0x63, 0x74, 0x6f, 0x70, 0x66, 0x61, 0x63, 0x65, 0x00
34+
};
35+
36+
byte notOKSessionName[] = {
37+
0xff, 0xff, 0x49, 0x4e, 0x00, 0x00, 0x00, 0x02, 0xcc, 0x0f, 0x6c, 0x49, 0x00, 0x00, 0xa4, 0x9b,
38+
0x6c, 0x61, 0x70, 0x70, 0x69, 0x65, 0x6d, 0x63, 0x74, 0x6f, 0x70, 0x66, 0x61, 0x63, 0x65, 0x2f,
39+
0x46, 0x4c, 0x55, 0x49, 0x44, 0x20, 0x53, 0x79, 0x6e, 0x74, 0x68, 0x20, 0x28, 0x36, 0x34, 0x37,
40+
0x38, 0x29, 0x2d, 0x53, 0x79, 0x6e, 0x74, 0x68, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x70,
41+
0x6f, 0x72, 0x74, 0x20, 0x28, 0x36, 0x34, 0x37, 0x38, 0x3a, 0x30, 0x29, 0x00
42+
};
43+
44+
write(okSessionName, sizeof(okSessionName));
45+
2746
}
2847

2948
if (port == (DEFAULT_CONTROL_PORT + 1) && true)
@@ -309,7 +328,7 @@ class EthernetUDP
309328

310329
byte slecht[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
311330

312-
write(rr2, sizeof(rr2));
331+
// write(rr2, sizeof(rr2));
313332
}
314333

315334
if (port == 5005 && true)
@@ -339,6 +358,17 @@ class EthernetUDP
339358
return _buffer.size();
340359
};
341360

361+
int read()
362+
{
363+
if (_buffer.size() == 0)
364+
return -1;
365+
366+
byte value = _buffer.front();
367+
_buffer.pop_front();
368+
369+
return value;
370+
}
371+
342372
size_t read(byte* buffer, size_t size)
343373
{
344374
size = min(size, _buffer.size());
@@ -363,68 +393,9 @@ class EthernetUDP
363393
};
364394

365395
void endPacket() { };
396+
366397
void flush()
367398
{
368-
if (_port == 5004)
369-
{
370-
if (_buffer[0] == 0xff && _buffer[1] == 0xff && _buffer[2] == 'I' &&_buffer[3] == 'N')
371-
{
372-
_buffer.clear();
373-
374-
375-
byte u[] = {
376-
0xff, 0xff,
377-
0x4f, 0x4b,
378-
0x00, 0x00, 0x00, 0x02,
379-
0xb7, 0x06, 0x20, 0x30,
380-
0xda, 0x8d, 0xc5, 0x8a,
381-
0x4d, 0x61, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x20, 0x50, 0x72, 0x6f, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x53, 0x61, 0x6e, 0x64, 0x72, 0x61, 0x20, 0x56, 0x65, 0x72, 0x62, 0x65, 0x6b, 0x65, 0x6e, 0x20, 0x28, 0x32, 0x29, 0x00 };
382-
383-
384-
385-
386-
387-
byte r[] = { 0xff, 0xff,
388-
0x4f, 0x4b,
389-
0x00, 0x0, 0x00, 0x02,
390-
0xb7, 0x06, 0x20, 0x30,
391-
0xda, 0x8d, 0xc5, 0x8a,
392-
0x53, 0x65, 0x73, 0x73, 0x69, 0x6, 0x6e, 0x31, 0x2d, 0x42, 0x00 };
393-
write(u, sizeof(u));
394-
}
395-
}
396-
if (_port == 5005)
397-
{
398-
if (_buffer[0] == 0xff && _buffer[1] == 0xff && _buffer[2] == 'I' &&_buffer[3] == 'N')
399-
{
400-
_buffer.clear();
401-
byte r[] = { 0xff, 0xff,
402-
0x4f, 0x4b,
403-
0x00, 0x0, 0x00, 0x02,
404-
0xb7, 0x06, 0x20, 0x30,
405-
0xda, 0x8d, 0xc5, 0x8a,
406-
0x53, 0x65, 0x73, 0x73, 0x69, 0x6, 0x6e, 0x31, 0x2d, 0x42, 0x00 };
407-
write(r, sizeof(r));
408-
}
409-
else if (_buffer[0] == 0xff && _buffer[1] == 0xff && _buffer[2] == 'C' &&_buffer[3] == 'K')
410-
{
411-
if (_buffer[8] == 0x00)
412-
{
413-
_buffer.clear();
414-
byte r[] = { 0xff, 0xff,
415-
0x43, 0x4b,
416-
0xda, 0x8d, 0xc5, 0x8a,
417-
0x01,
418-
0x65, 0x73, 0x73,
419-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x34,
420-
0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x6c, 0x83,
421-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
422-
write(r, sizeof(r));
423-
}
424-
else
425-
_buffer.clear();
426-
}
427-
}
428399
};
429400

430401
void stop() { _buffer.clear(); };

0 commit comments

Comments
 (0)