mqtt: ensure we do not request extra data after buffering

This addresses Redmine bug #5018 by ensuring that the parser
never requests additional data via the Incomplete error, but to
raise an actual parse error, since it is supposed to have all
the data as specified by the message length in the header already.
pull/7223/head
Sascha Steinbiss 4 years ago committed by Victor Julien
parent e3180e3248
commit 5618273ef4

@ -30,7 +30,7 @@ use nom7::sequence::tuple;
use nom7::{Err, IResult, Needed}; use nom7::{Err, IResult, Needed};
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
#[derive(Debug)] #[derive(Copy, Clone, Debug)]
pub struct FixedHeader { pub struct FixedHeader {
pub message_type: MQTTTypeCode, pub message_type: MQTTTypeCode,
pub dup_flag: bool, pub dup_flag: bool,
@ -222,94 +222,108 @@ pub fn parse_connect(i: &[u8]) -> IResult<&[u8], MQTTConnectData> {
)) ))
} }
pub fn parse_connack(i: &[u8], protocol_version: u8) -> IResult<&[u8], MQTTConnackData> { #[inline]
let (i, topic_name_compression_response) = be_u8(i)?; pub fn parse_connack(protocol_version: u8) -> impl Fn(&[u8]) -> IResult<&[u8], MQTTConnackData>
let (i, return_code) = be_u8(i)?; where
let (i, properties) = parse_properties(i, protocol_version == 5)?; {
Ok(( move |i: &[u8]| {
i, let (i, topic_name_compression_response) = be_u8(i)?;
MQTTConnackData { let (i, return_code) = be_u8(i)?;
session_present: (topic_name_compression_response & 1) != 0, let (i, properties) = parse_properties(i, protocol_version == 5)?;
return_code, Ok((
properties, i,
}, MQTTConnackData {
)) session_present: (topic_name_compression_response & 1) != 0,
return_code,
properties,
},
))
}
} }
pub fn parse_publish( #[inline]
i: &[u8], protocol_version: u8, has_id: bool, fn parse_publish(
) -> IResult<&[u8], MQTTPublishData> { protocol_version: u8, has_id: bool,
let (i, topic) = parse_mqtt_string(i)?; ) -> impl Fn(&[u8]) -> IResult<&[u8], MQTTPublishData>
let (i, message_id) = cond(has_id, be_u16)(i)?; where
let (message, properties) = parse_properties(i, protocol_version == 5)?; {
Ok(( move |i: &[u8]| {
i, let (i, topic) = parse_mqtt_string(i)?;
MQTTPublishData { let (i, message_id) = cond(has_id, be_u16)(i)?;
topic, let (message, properties) = parse_properties(i, protocol_version == 5)?;
message_id, Ok((
message: message.to_vec(), i,
properties, MQTTPublishData {
}, topic,
)) message_id,
message: message.to_vec(),
properties,
},
))
}
} }
#[inline] #[inline]
fn parse_msgidonly(input: &[u8], protocol_version: u8) -> IResult<&[u8], MQTTMessageIdOnly> { pub fn parse_msgidonly(protocol_version: u8) -> impl Fn(&[u8]) -> IResult<&[u8], MQTTMessageIdOnly>
if protocol_version < 5 { where
// before v5 we don't even have to care about reason codes {
// and properties, lucky us move |input: &[u8]| {
return parse_msgidonly_v3(input); if protocol_version < 5 {
} // before v5 we don't even have to care about reason codes
let remaining_len = input.len(); // and properties, lucky us
match be_u16(input) { return parse_msgidonly_v3(input);
Ok((rem, message_id)) => { }
if remaining_len == 2 { let remaining_len = input.len();
// from the spec: " The Reason Code and Property Length can be match be_u16(input) {
// omitted if the Reason Code is 0x00 (Success) and there are Ok((rem, message_id)) => {
// no Properties. In this case the message has a Remaining if remaining_len == 2 {
// Length of 2." // from the spec: " The Reason Code and Property Length can be
return Ok(( // omitted if the Reason Code is 0x00 (Success) and there are
rem, // no Properties. In this case the message has a Remaining
MQTTMessageIdOnly { // Length of 2."
message_id, return Ok((
reason_code: Some(0), rem,
properties: None, MQTTMessageIdOnly {
}, message_id,
)); reason_code: Some(0),
} properties: None,
match be_u8(rem) { },
Ok((rem, reason_code)) => { ));
// We are checking for 3 because in that case we have a }
// header plus reason code, but no properties. match be_u8(rem) {
if remaining_len == 3 { Ok((rem, reason_code)) => {
// no properties // We are checking for 3 because in that case we have a
return Ok(( // header plus reason code, but no properties.
rem, if remaining_len == 3 {
MQTTMessageIdOnly { // no properties
message_id,
reason_code: Some(reason_code),
properties: None,
},
));
}
match parse_properties(rem, true) {
Ok((rem, properties)) => {
return Ok(( return Ok((
rem, rem,
MQTTMessageIdOnly { MQTTMessageIdOnly {
message_id, message_id,
reason_code: Some(reason_code), reason_code: Some(reason_code),
properties, properties: None,
}, },
)); ));
} }
Err(e) => return Err(e), match parse_properties(rem, true) {
Ok((rem, properties)) => {
return Ok((
rem,
MQTTMessageIdOnly {
message_id,
reason_code: Some(reason_code),
properties,
},
));
}
Err(e) => return Err(e),
}
} }
Err(e) => return Err(e),
} }
Err(e) => return Err(e),
} }
Err(e) => return Err(e),
} }
Err(e) => return Err(e),
} }
} }
@ -333,114 +347,140 @@ pub fn parse_subscribe_topic(i: &[u8]) -> IResult<&[u8], MQTTSubscribeTopicData>
Ok((i, MQTTSubscribeTopicData { topic_name, qos })) Ok((i, MQTTSubscribeTopicData { topic_name, qos }))
} }
pub fn parse_subscribe(i: &[u8], protocol_version: u8) -> IResult<&[u8], MQTTSubscribeData> { #[inline]
let (i, message_id) = be_u16(i)?; pub fn parse_subscribe(protocol_version: u8) -> impl Fn(&[u8]) -> IResult<&[u8], MQTTSubscribeData>
let (i, properties) = parse_properties(i, protocol_version == 5)?; where
let (i, topics) = many1(complete(parse_subscribe_topic))(i)?; {
Ok(( move |i: &[u8]| {
i, let (i, message_id) = be_u16(i)?;
MQTTSubscribeData { let (i, properties) = parse_properties(i, protocol_version == 5)?;
message_id, let (i, topics) = many1(complete(parse_subscribe_topic))(i)?;
topics, Ok((
properties, i,
}, MQTTSubscribeData {
)) message_id,
topics,
properties,
},
))
}
} }
pub fn parse_suback(i: &[u8], protocol_version: u8) -> IResult<&[u8], MQTTSubackData> { #[inline]
let (i, message_id) = be_u16(i)?; pub fn parse_suback(protocol_version: u8) -> impl Fn(&[u8]) -> IResult<&[u8], MQTTSubackData>
let (qoss, properties) = parse_properties(i, protocol_version == 5)?; where
Ok(( {
i, move |i: &[u8]| {
MQTTSubackData { let (i, message_id) = be_u16(i)?;
message_id, let (qoss, properties) = parse_properties(i, protocol_version == 5)?;
qoss: qoss.to_vec(), Ok((
properties, i,
}, MQTTSubackData {
)) message_id,
qoss: qoss.to_vec(),
properties,
},
))
}
} }
pub fn parse_unsubscribe(i: &[u8], protocol_version: u8) -> IResult<&[u8], MQTTUnsubscribeData> { #[inline]
let (i, message_id) = be_u16(i)?; pub fn parse_unsubscribe(
let (i, properties) = parse_properties(i, protocol_version == 5)?; protocol_version: u8,
let (i, topics) = many0(complete(parse_mqtt_string))(i)?; ) -> impl Fn(&[u8]) -> IResult<&[u8], MQTTUnsubscribeData>
Ok(( where
i, {
MQTTUnsubscribeData { move |i: &[u8]| {
message_id, let (i, message_id) = be_u16(i)?;
topics, let (i, properties) = parse_properties(i, protocol_version == 5)?;
properties, let (i, topics) = many0(complete(parse_mqtt_string))(i)?;
}, Ok((
)) i,
MQTTUnsubscribeData {
message_id,
topics,
properties,
},
))
}
} }
pub fn parse_unsuback(i: &[u8], protocol_version: u8) -> IResult<&[u8], MQTTUnsubackData> { #[inline]
let (i, message_id) = be_u16(i)?; pub fn parse_unsuback(protocol_version: u8) -> impl Fn(&[u8]) -> IResult<&[u8], MQTTUnsubackData>
let (i, properties) = parse_properties(i, protocol_version == 5)?; where
let (i, reason_codes) = many0(complete(be_u8))(i)?; {
Ok(( move |i: &[u8]| {
i, let (i, message_id) = be_u16(i)?;
MQTTUnsubackData { let (i, properties) = parse_properties(i, protocol_version == 5)?;
message_id, let (i, reason_codes) = many0(complete(be_u8))(i)?;
properties, Ok((
reason_codes: Some(reason_codes), i,
}, MQTTUnsubackData {
)) message_id,
properties,
reason_codes: Some(reason_codes),
},
))
}
} }
#[inline] #[inline]
fn parse_disconnect( fn parse_disconnect(
input: &[u8], remaining_len: usize, protocol_version: u8, remaining_len: usize, protocol_version: u8,
) -> IResult<&[u8], MQTTDisconnectData> { ) -> impl Fn(&[u8]) -> IResult<&[u8], MQTTDisconnectData>
if protocol_version < 5 { where
return Ok(( {
input, move |input: &[u8]| {
MQTTDisconnectData { if protocol_version < 5 {
reason_code: None, return Ok((
properties: None, input,
}, MQTTDisconnectData {
)); reason_code: None,
} properties: None,
if remaining_len == 0 { },
// The Reason Code and Property Length can be omitted if the Reason ));
// Code is 0x00 (Normal disconnection) and there are no Properties. }
// In this case the DISCONNECT has a Remaining Length of 0. if remaining_len == 0 {
return Ok(( // The Reason Code and Property Length can be omitted if the Reason
input, // Code is 0x00 (Normal disconnection) and there are no Properties.
MQTTDisconnectData { // In this case the DISCONNECT has a Remaining Length of 0.
reason_code: Some(0), return Ok((
properties: None, input,
}, MQTTDisconnectData {
)); reason_code: Some(0),
} properties: None,
match be_u8(input) { },
Ok((rem, reason_code)) => { ));
// We are checking for 1 because in that case we have a }
// header plus reason code, but no properties. match be_u8(input) {
if remaining_len == 1 { Ok((rem, reason_code)) => {
// no properties // We are checking for 1 because in that case we have a
return Ok(( // header plus reason code, but no properties.
rem, if remaining_len == 1 {
MQTTDisconnectData { // no properties
reason_code: Some(0),
properties: None,
},
));
}
match parse_properties(rem, true) {
Ok((rem, properties)) => {
return Ok(( return Ok((
rem, rem,
MQTTDisconnectData { MQTTDisconnectData {
reason_code: Some(reason_code), reason_code: Some(0),
properties, properties: None,
}, },
)); ));
} }
Err(e) => return Err(e), match parse_properties(rem, true) {
Ok((rem, properties)) => {
return Ok((
rem,
MQTTDisconnectData {
reason_code: Some(reason_code),
properties,
},
));
}
Err(e) => return Err(e),
}
} }
Err(e) => return Err(e),
} }
Err(e) => return Err(e),
} }
} }
@ -457,6 +497,151 @@ pub fn parse_auth(i: &[u8]) -> IResult<&[u8], MQTTAuthData> {
)) ))
} }
#[inline]
fn parse_remaining_message<'a>(
full: &'a [u8], len: usize, skiplen: usize, header: FixedHeader, message_type: MQTTTypeCode,
protocol_version: u8,
) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], MQTTMessage>
where
{
move |input: &'a [u8]| {
match message_type {
MQTTTypeCode::CONNECT => match parse_connect(input) {
Ok((_rem, conn)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::CONNECT(conn),
};
Ok((&full[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::CONNACK => match parse_connack(protocol_version)(input) {
Ok((_rem, connack)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::CONNACK(connack),
};
Ok((&full[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::PUBLISH => {
match parse_publish(protocol_version, header.qos_level > 0)(input) {
Ok((_rem, publish)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::PUBLISH(publish),
};
Ok((&full[skiplen + len..], msg))
}
Err(e) => Err(e),
}
}
MQTTTypeCode::PUBACK
| MQTTTypeCode::PUBREC
| MQTTTypeCode::PUBREL
| MQTTTypeCode::PUBCOMP => match parse_msgidonly(protocol_version)(input) {
Ok((_rem, msgidonly)) => {
let msg = MQTTMessage {
header,
op: match message_type {
MQTTTypeCode::PUBACK => MQTTOperation::PUBACK(msgidonly),
MQTTTypeCode::PUBREC => MQTTOperation::PUBREC(msgidonly),
MQTTTypeCode::PUBREL => MQTTOperation::PUBREL(msgidonly),
MQTTTypeCode::PUBCOMP => MQTTOperation::PUBCOMP(msgidonly),
_ => MQTTOperation::UNASSIGNED,
},
};
Ok((&full[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::SUBSCRIBE => match parse_subscribe(protocol_version)(input) {
Ok((_rem, subs)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::SUBSCRIBE(subs),
};
Ok((&full[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::SUBACK => match parse_suback(protocol_version)(input) {
Ok((_rem, suback)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::SUBACK(suback),
};
Ok((&full[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::UNSUBSCRIBE => match parse_unsubscribe(protocol_version)(input) {
Ok((_rem, unsub)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::UNSUBSCRIBE(unsub),
};
Ok((&full[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::UNSUBACK => match parse_unsuback(protocol_version)(input) {
Ok((_rem, unsuback)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::UNSUBACK(unsuback),
};
Ok((&full[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::PINGREQ | MQTTTypeCode::PINGRESP => {
let msg = MQTTMessage {
header,
op: match message_type {
MQTTTypeCode::PINGREQ => MQTTOperation::PINGREQ,
MQTTTypeCode::PINGRESP => MQTTOperation::PINGRESP,
_ => MQTTOperation::UNASSIGNED,
},
};
Ok((&full[skiplen + len..], msg))
}
MQTTTypeCode::DISCONNECT => match parse_disconnect(len, protocol_version)(input) {
Ok((_rem, disco)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::DISCONNECT(disco),
};
Ok((&full[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::AUTH => match parse_auth(input) {
Ok((_rem, auth)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::AUTH(auth),
};
Ok((&full[skiplen + len..], msg))
}
Err(e) => Err(e),
},
// Unassigned message type code. Unlikely to happen with
// regular traffic, might be an indication for broken or
// crafted MQTT traffic.
_ => {
let msg = MQTTMessage {
header,
op: MQTTOperation::UNASSIGNED,
};
return Ok((&full[skiplen + len..], msg));
}
}
}
}
pub fn parse_message( pub fn parse_message(
input: &[u8], protocol_version: u8, max_msg_size: usize, input: &[u8], protocol_version: u8, max_msg_size: usize,
) -> IResult<&[u8], MQTTMessage> { ) -> IResult<&[u8], MQTTMessage> {
@ -500,143 +685,22 @@ pub fn parse_message(
// Parse the contents of the buffer into a single message. // Parse the contents of the buffer into a single message.
// We reslice the remainder into the portion that we are interested // We reslice the remainder into the portion that we are interested
// in, according to the length we just parsed. This helps with the // in, according to the length we just parsed. This helps with the
// complete! parsers, where we would otherwise need to keep track // complete() parsers, where we would otherwise need to keep track
// of the already parsed length. // of the already parsed length.
let rem = &fullrem[..len]; let rem = &fullrem[..len];
match message_type {
MQTTTypeCode::CONNECT => match parse_connect(rem) { // Parse remaining message in buffer. We use complete() to ensure
Ok((_rem, conn)) => { // we do not request additional content in case of incomplete
let msg = MQTTMessage { // parsing, but raise an error instead as we should have all the
header, // data asked for in the header.
op: MQTTOperation::CONNECT(conn), return complete(parse_remaining_message(
}; input,
Ok((&input[skiplen + len..], msg)) len,
} skiplen,
Err(e) => Err(e), header,
}, message_type,
MQTTTypeCode::CONNACK => match parse_connack(rem, protocol_version) { protocol_version,
Ok((_rem, connack)) => { ))(rem);
let msg = MQTTMessage {
header,
op: MQTTOperation::CONNACK(connack),
};
Ok((&input[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::PUBLISH => {
match parse_publish(rem, protocol_version, header.qos_level > 0) {
Ok((_rem, publish)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::PUBLISH(publish),
};
Ok((&input[skiplen + len..], msg))
}
Err(e) => Err(e),
}
}
MQTTTypeCode::PUBACK
| MQTTTypeCode::PUBREC
| MQTTTypeCode::PUBREL
| MQTTTypeCode::PUBCOMP => match parse_msgidonly(rem, protocol_version) {
Ok((_rem, msgidonly)) => {
let msg = MQTTMessage {
header,
op: match message_type {
MQTTTypeCode::PUBACK => MQTTOperation::PUBACK(msgidonly),
MQTTTypeCode::PUBREC => MQTTOperation::PUBREC(msgidonly),
MQTTTypeCode::PUBREL => MQTTOperation::PUBREL(msgidonly),
MQTTTypeCode::PUBCOMP => MQTTOperation::PUBCOMP(msgidonly),
_ => MQTTOperation::UNASSIGNED,
},
};
Ok((&input[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::SUBSCRIBE => match parse_subscribe(rem, protocol_version) {
Ok((_rem, subs)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::SUBSCRIBE(subs),
};
Ok((&input[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::SUBACK => match parse_suback(rem, protocol_version) {
Ok((_rem, suback)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::SUBACK(suback),
};
Ok((&input[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::UNSUBSCRIBE => match parse_unsubscribe(rem, protocol_version) {
Ok((_rem, unsub)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::UNSUBSCRIBE(unsub),
};
Ok((&input[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::UNSUBACK => match parse_unsuback(rem, protocol_version) {
Ok((_rem, unsuback)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::UNSUBACK(unsuback),
};
Ok((&input[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::PINGREQ | MQTTTypeCode::PINGRESP => {
let msg = MQTTMessage {
header,
op: match message_type {
MQTTTypeCode::PINGREQ => MQTTOperation::PINGREQ,
MQTTTypeCode::PINGRESP => MQTTOperation::PINGRESP,
_ => MQTTOperation::UNASSIGNED,
},
};
return Ok((&input[skiplen + len..], msg));
}
MQTTTypeCode::DISCONNECT => match parse_disconnect(rem, len, protocol_version) {
Ok((_rem, disco)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::DISCONNECT(disco),
};
Ok((&input[skiplen + len..], msg))
}
Err(e) => Err(e),
},
MQTTTypeCode::AUTH => match parse_auth(rem) {
Ok((_rem, auth)) => {
let msg = MQTTMessage {
header,
op: MQTTOperation::AUTH(auth),
};
Ok((&input[skiplen + len..], msg))
}
Err(e) => Err(e),
},
// Unassigned message type code. Unlikely to happen with
// regular traffic, might be an indication for broken or
// crafted MQTT traffic.
_ => {
let msg = MQTTMessage {
header,
op: MQTTOperation::UNASSIGNED,
};
return Ok((&rem[len..], msg));
}
}
} }
Err(err) => { Err(err) => {
return Err(err); return Err(err);

Loading…
Cancel
Save