use crate::buffer::Buffer;
use crate::types::{ErrorCode, Result};
const MAX_SO_NAME_BYTES: usize = 256;
const MAX_SO_EVENTS: usize = 256;
const MAX_SO_EVENT_DATA_BYTES: usize = 64 * 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SharedObjectEventType {
Use,
Release,
RequestChange,
Change,
Success,
SendMessage,
Status,
Clear,
Remove,
RequestRemove,
UseSuccess,
Unknown(u8),
}
impl SharedObjectEventType {
fn from_byte(b: u8) -> Self {
match b {
1 => Self::Use,
2 => Self::Release,
3 => Self::RequestChange,
4 => Self::Change,
5 => Self::Success,
6 => Self::SendMessage,
7 => Self::Status,
8 => Self::Clear,
9 => Self::Remove,
10 => Self::RequestRemove,
11 => Self::UseSuccess,
other => Self::Unknown(other),
}
}
fn to_byte(self) -> u8 {
match self {
Self::Use => 1,
Self::Release => 2,
Self::RequestChange => 3,
Self::Change => 4,
Self::Success => 5,
Self::SendMessage => 6,
Self::Status => 7,
Self::Clear => 8,
Self::Remove => 9,
Self::RequestRemove => 10,
Self::UseSuccess => 11,
Self::Unknown(b) => b,
}
}
}
#[derive(Debug, Clone)]
pub struct SharedObjectEvent {
pub event_type: SharedObjectEventType,
pub data: Vec<u8>,
}
#[derive(Debug, Clone, Default)]
pub struct SharedObjectMessage {
pub name: String,
pub version: u32,
pub flags: u32,
pub events: Vec<SharedObjectEvent>,
}
impl SharedObjectMessage {
pub fn is_persistent(&self) -> bool {
self.flags & 0x01 != 0
}
}
pub fn parse(data: &[u8]) -> Result<SharedObjectMessage> {
let mut buf = Buffer::from_slice(data);
let name_len = read_u16(&mut buf)? as usize;
if name_len > MAX_SO_NAME_BYTES || buf.available() < name_len {
return Err(ErrorCode::Amf);
}
let mut name_bytes = vec![0u8; name_len];
buf.read(&mut name_bytes).map_err(|_| ErrorCode::Amf)?;
let name = String::from_utf8(name_bytes).map_err(|_| ErrorCode::Amf)?;
let version = read_u32(&mut buf)?;
let flags = read_u32(&mut buf)?;
let _reserved = read_u32(&mut buf)?;
let mut events = Vec::new();
while buf.available() > 0 {
if events.len() >= MAX_SO_EVENTS {
return Err(ErrorCode::Amf);
}
let mut ty_byte = [0u8; 1];
buf.read(&mut ty_byte).map_err(|_| ErrorCode::Amf)?;
let event_len = read_u32(&mut buf)? as usize;
if event_len > MAX_SO_EVENT_DATA_BYTES || buf.available() < event_len {
return Err(ErrorCode::Amf);
}
let mut event_data = vec![0u8; event_len];
buf.read(&mut event_data).map_err(|_| ErrorCode::Amf)?;
events.push(SharedObjectEvent {
event_type: SharedObjectEventType::from_byte(ty_byte[0]),
data: event_data,
});
}
Ok(SharedObjectMessage {
name,
version,
flags,
events,
})
}
pub fn write(msg: &SharedObjectMessage, buf: &mut Buffer) -> Result<()> {
if msg.name.len() > MAX_SO_NAME_BYTES {
return Err(ErrorCode::Amf);
}
if msg.events.len() > MAX_SO_EVENTS {
return Err(ErrorCode::Amf);
}
buf.write(&(msg.name.len() as u16).to_be_bytes())
.map_err(|_| ErrorCode::Internal)?;
buf.write(msg.name.as_bytes())
.map_err(|_| ErrorCode::Internal)?;
buf.write(&msg.version.to_be_bytes())
.map_err(|_| ErrorCode::Internal)?;
buf.write(&msg.flags.to_be_bytes())
.map_err(|_| ErrorCode::Internal)?;
buf.write(&0u32.to_be_bytes())
.map_err(|_| ErrorCode::Internal)?;
for event in &msg.events {
if event.data.len() > MAX_SO_EVENT_DATA_BYTES {
return Err(ErrorCode::Amf);
}
buf.write(&[event.event_type.to_byte()])
.map_err(|_| ErrorCode::Internal)?;
buf.write(&(event.data.len() as u32).to_be_bytes())
.map_err(|_| ErrorCode::Internal)?;
buf.write(&event.data).map_err(|_| ErrorCode::Internal)?;
}
Ok(())
}
fn read_u16(buf: &mut Buffer) -> Result<u16> {
let mut b = [0u8; 2];
buf.read(&mut b).map_err(|_| ErrorCode::Amf)?;
Ok(u16::from_be_bytes(b))
}
fn read_u32(buf: &mut Buffer) -> Result<u32> {
let mut b = [0u8; 4];
buf.read(&mut b).map_err(|_| ErrorCode::Amf)?;
Ok(u32::from_be_bytes(b))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trips_header_with_no_events() {
let msg = SharedObjectMessage {
name: "chat".to_string(),
version: 1,
flags: 0x01,
events: Vec::new(),
};
let mut buf = Buffer::new();
write(&msg, &mut buf).unwrap();
let parsed = parse(buf.peek()).unwrap();
assert_eq!(parsed.name, "chat");
assert_eq!(parsed.version, 1);
assert!(parsed.is_persistent());
assert!(parsed.events.is_empty());
}
#[test]
fn round_trips_events_with_known_and_unknown_types() {
let msg = SharedObjectMessage {
name: "scoreboard".to_string(),
version: 3,
flags: 0,
events: vec![
SharedObjectEvent {
event_type: SharedObjectEventType::Use,
data: Vec::new(),
},
SharedObjectEvent {
event_type: SharedObjectEventType::Change,
data: vec![0xDE, 0xAD, 0xBE, 0xEF],
},
SharedObjectEvent {
event_type: SharedObjectEventType::Unknown(200),
data: vec![1, 2, 3],
},
],
};
let mut buf = Buffer::new();
write(&msg, &mut buf).unwrap();
let parsed = parse(buf.peek()).unwrap();
assert_eq!(parsed.events.len(), 3);
assert_eq!(parsed.events[0].event_type, SharedObjectEventType::Use);
assert!(parsed.events[0].data.is_empty());
assert_eq!(parsed.events[1].event_type, SharedObjectEventType::Change);
assert_eq!(parsed.events[1].data, vec![0xDE, 0xAD, 0xBE, 0xEF]);
assert_eq!(
parsed.events[2].event_type,
SharedObjectEventType::Unknown(200)
);
assert_eq!(parsed.events[2].data, vec![1, 2, 3]);
}
#[test]
fn rejects_name_length_exceeding_remaining_buffer() {
let mut buf = Buffer::new();
buf.write(&500u16.to_be_bytes()).unwrap();
buf.write(b"short").unwrap();
assert!(parse(buf.peek()).is_err());
}
#[test]
fn rejects_event_length_exceeding_remaining_buffer() {
let mut buf = Buffer::new();
buf.write(&4u16.to_be_bytes()).unwrap();
buf.write(b"test").unwrap();
buf.write(&1u32.to_be_bytes()).unwrap(); buf.write(&0u32.to_be_bytes()).unwrap(); buf.write(&0u32.to_be_bytes()).unwrap(); buf.write(&[SharedObjectEventType::Change.to_byte()])
.unwrap();
buf.write(&1_000_000u32.to_be_bytes()).unwrap(); assert!(parse(buf.peek()).is_err());
}
#[test]
fn rejects_truncated_header() {
let mut buf = Buffer::new();
buf.write(&4u16.to_be_bytes()).unwrap();
buf.write(b"test").unwrap();
assert!(parse(buf.peek()).is_err());
}
#[test]
fn rejects_invalid_utf8_name_instead_of_substituting() {
let mut buf = Buffer::new();
let invalid_name = [0xFFu8, 0xFE];
buf.write(&(invalid_name.len() as u16).to_be_bytes())
.unwrap();
buf.write(&invalid_name).unwrap();
buf.write(&0u32.to_be_bytes()).unwrap(); buf.write(&0u32.to_be_bytes()).unwrap(); buf.write(&0u32.to_be_bytes()).unwrap(); assert!(parse(buf.peek()).is_err());
}
#[test]
fn write_rejects_more_events_than_parse_would_accept() {
let events = (0..(MAX_SO_EVENTS + 1))
.map(|_| SharedObjectEvent {
event_type: SharedObjectEventType::Use,
data: Vec::new(),
})
.collect();
let msg = SharedObjectMessage {
name: "chat".to_string(),
version: 1,
flags: 0,
events,
};
let mut buf = Buffer::new();
assert!(write(&msg, &mut buf).is_err());
}
}