use byteorder::{ByteOrder, LittleEndian};
use bytes::{Buf, BufMut, Bytes, BytesMut};
const MASSIVE_PACKET_OPCODE: u16 = 0x600D;
const ENCRYPTED_ALIGNMENT: usize = 8;
fn find_encrypted_length(given_length: usize) -> usize {
let aligned_length = given_length % ENCRYPTED_ALIGNMENT;
if aligned_length == 0 {
return given_length;
}
given_length + (8 - aligned_length) }
#[derive(Eq, PartialEq, Debug)]
pub enum SilkroadFrame {
Packet {
count: u8,
crc: u8,
opcode: u16,
data: Bytes,
},
Encrypted {
content_size: usize,
encrypted_data: Bytes,
},
MassiveHeader {
count: u8,
crc: u8,
contained_opcode: u16,
contained_count: u16,
},
MassiveContainer { count: u8, crc: u8, inner: Bytes },
}
impl SilkroadFrame {
pub fn parse(data: &[u8]) -> Result<(usize, SilkroadFrame), usize> {
if data.len() < 2 {
return Err(2 - data.len());
}
let length = LittleEndian::read_u16(&data[0..2]);
let encrypted = length & 0x8000 != 0;
let content_size = (length & 0x7FFF) as usize;
let total_size = if encrypted {
find_encrypted_length(content_size + 4)
} else {
content_size + 4
};
let data = &data[2..];
if data.len() < total_size {
return Err(total_size - data.len());
}
let total_consumed = total_size + 2;
let data = Bytes::copy_from_slice(&data[0..total_size]);
if encrypted {
return Ok((
total_consumed,
SilkroadFrame::Encrypted {
content_size,
encrypted_data: data,
},
));
}
Ok((total_consumed, Self::from_data(&data)))
}
pub fn from_data(data: &[u8]) -> SilkroadFrame {
assert!(data.len() >= 4);
let opcode = LittleEndian::read_u16(&data[0..2]);
let count = data[2];
let crc = data[3];
if opcode == MASSIVE_PACKET_OPCODE {
assert!(data.len() >= 5);
let mode = data[4];
if mode == 1 {
assert!(data.len() >= 10);
let inner_amount = LittleEndian::read_u16(&data[5..7]);
let inner_opcode = LittleEndian::read_u16(&data[7..9]);
SilkroadFrame::MassiveHeader {
count,
crc,
contained_opcode: inner_opcode,
contained_count: inner_amount,
}
} else {
SilkroadFrame::MassiveContainer {
count,
crc,
inner: Bytes::copy_from_slice(&data[5..]),
}
}
} else {
SilkroadFrame::Packet {
count,
crc,
opcode,
data: Bytes::copy_from_slice(&data[4..]),
}
}
}
pub fn content_size(&self) -> usize {
match &self {
SilkroadFrame::Packet { data, .. } => data.len(),
SilkroadFrame::Encrypted { content_size, .. } => *content_size,
SilkroadFrame::MassiveHeader { .. } => {
6
}
SilkroadFrame::MassiveContainer { inner, .. } => {
1 + inner.len()
}
}
}
pub fn packet_size(&self) -> usize {
match self {
SilkroadFrame::Encrypted { content_size, .. } => {
find_encrypted_length(*content_size + 4) + 2
}
_ => 6 + self.content_size(),
}
}
pub fn opcode(&self) -> Option<u16> {
match &self {
SilkroadFrame::Packet { opcode, .. } => Some(*opcode),
SilkroadFrame::Encrypted { .. } => None,
_ => Some(0x600D),
}
}
pub fn serialize(&self) -> Bytes {
let mut output = BytesMut::with_capacity(self.packet_size());
match &self {
SilkroadFrame::Packet {
count,
crc,
opcode,
data,
} => {
output.put_u16_le(self.content_size() as u16);
output.put_u16_le(*opcode);
output.put_u8(*count);
output.put_u8(*crc);
output.put_slice(data);
}
SilkroadFrame::Encrypted {
content_size,
encrypted_data,
} => {
output.put_u16_le((*content_size | 0x8000) as u16);
output.put_slice(encrypted_data);
}
SilkroadFrame::MassiveHeader {
count,
crc,
contained_opcode,
contained_count,
} => {
output.put_u16_le(self.content_size() as u16);
output.put_u16_le(MASSIVE_PACKET_OPCODE);
output.put_u8(*count);
output.put_u8(*crc);
output.put_u8(1);
output.put_u16_le(*contained_count);
output.put_u16_le(*contained_opcode);
output.put_u8(0);
}
SilkroadFrame::MassiveContainer { count, crc, inner } => {
output.put_u16_le(self.content_size() as u16);
output.put_u16_le(MASSIVE_PACKET_OPCODE);
output.put_u8(*count);
output.put_u8(*crc);
output.put_u8(0);
output.put_slice(inner);
}
}
output.freeze()
}
}
#[cfg(feature = "codec")]
pub use codec::*;
#[cfg(feature = "codec")]
mod codec {
use super::*;
use std::io;
use tokio_util::codec::{Decoder, Encoder};
pub struct SilkroadCodec;
impl Encoder<SilkroadFrame> for SilkroadCodec {
type Error = io::Error;
fn encode(&mut self, item: SilkroadFrame, dst: &mut BytesMut) -> Result<(), Self::Error> {
let bytes = item.serialize();
dst.extend_from_slice(&bytes);
Ok(())
}
}
impl Decoder for SilkroadCodec {
type Item = SilkroadFrame;
type Error = io::Error;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
match SilkroadFrame::parse(src) {
Ok((bytes_read, frame)) => {
src.advance(bytes_read);
Ok(Some(frame))
}
Err(_) => Ok(None),
}
}
}
}
#[cfg(test)]
mod test {
use crate::{SilkroadCodec, SilkroadFrame};
use bytes::{Bytes, BytesMut};
use tokio_util::codec::Decoder;
#[test]
fn test_parse_empty() {
let data = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let (consumed, packet) =
SilkroadFrame::parse(&data).expect("Should parse empty, valid data");
assert_eq!(6, consumed);
assert_eq!(
SilkroadFrame::Packet {
count: 0,
crc: 0,
opcode: 0,
data: Bytes::new(),
},
packet
);
}
#[test]
fn test_parse_incomplete() {
let data = [0x00, 0x00, 0x00, 0x00, 0x00];
let res = SilkroadFrame::parse(&data);
assert!(matches!(res, Err(1)));
let data = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
let res = SilkroadFrame::parse(&data);
assert!(matches!(res, Err(1)));
}
#[test]
fn test_parse_content() {
let data = [0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01];
let (consumed, packet) = SilkroadFrame::parse(&data).expect("Should parse valid data");
assert_eq!(8, consumed);
assert_eq!(
SilkroadFrame::Packet {
count: 0,
crc: 0,
opcode: 0x0001,
data: Bytes::from_static(&[0x01, 0x01]),
},
packet
);
}
#[test]
fn test_parse_encrypted() {
let data = [0x02, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01];
let (consumed, packet) = SilkroadFrame::parse(&data).expect("Should parse valid data");
assert_eq!(10, consumed);
assert_eq!(
SilkroadFrame::Encrypted {
content_size: 2,
encrypted_data: Bytes::from_static(&[
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
]),
},
packet
);
}
#[test]
fn test_parse_massive() {
let header = [
0x06, 0x00, 0x0D, 0x60, 0x00, 0x00, 0x01, 0x01, 0x00, 0x42, 0x00, 0x00,
];
let (consumed, packet) = SilkroadFrame::parse(&header).expect("Should parse valid data");
assert_eq!(12, consumed);
assert_eq!(
SilkroadFrame::MassiveHeader {
count: 0,
crc: 0,
contained_opcode: 0x42,
contained_count: 1,
},
packet
);
let header = [0x02, 0x00, 0x0D, 0x60, 0x00, 0x00, 0x00, 0x01];
let (consumed, packet) = SilkroadFrame::parse(&header).expect("Should parse valid data");
assert_eq!(8, consumed);
assert_eq!(
SilkroadFrame::MassiveContainer {
count: 0,
crc: 0,
inner: Bytes::from_static(&[0x01]),
},
packet
);
}
#[test]
fn test_decoder() {
let mut codec = SilkroadCodec;
let mut buffer = BytesMut::new();
buffer.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]);
let decoded = codec.decode(&mut buffer);
assert!(matches!(decoded, Ok(None)));
buffer.extend_from_slice(&[0x00, 0x00]);
let decoded = codec.decode_eof(&mut buffer).unwrap();
assert_eq!(
Some(SilkroadFrame::Packet {
count: 0,
crc: 0,
opcode: 0,
data: Bytes::new(),
}),
decoded
);
}
#[test]
fn test_serialize_empty() {
let data = SilkroadFrame::Packet {
count: 0,
crc: 0,
opcode: 0,
data: Bytes::new(),
}
.serialize();
assert_eq!(data.as_ref(), &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
}
#[test]
fn test_serialize_encrypted() {
let data = SilkroadFrame::Encrypted {
content_size: 0,
encrypted_data: Bytes::from_static(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
}
.serialize();
assert_eq!(
data.as_ref(),
&[0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
);
}
#[test]
fn test_serialize_massive() {
let data = SilkroadFrame::MassiveHeader {
count: 0,
crc: 0,
contained_opcode: 0x42,
contained_count: 1,
}
.serialize();
assert_eq!(
data.as_ref(),
&[0x06, 0x00, 0x0D, 0x60, 0x00, 0x00, 0x01, 0x01, 0x00, 0x42, 0x00, 0x00]
);
let data = SilkroadFrame::MassiveContainer {
count: 0,
crc: 0,
inner: Bytes::new(),
}
.serialize();
assert_eq!(data.as_ref(), &[0x01, 0x00, 0x0D, 0x60, 0x00, 0x00, 0x00]);
}
}