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,
},
}Expand description
A ‘frame’ denotes the most fundamental block of data that can be sent between the client and the server in Silkroad Online. Any and all operations or data exchanges are built on top of something like a frame.
There are two categories of frames; normal frames and massive frames. A normal frame is the most common frame denoting a single operation using a specified opcode. This frame may be encrypted, causing everything but the length to require decrypting before being usable. Massive frames are used to carry a larger logical payload. A massive header is sent first, containing the number of following container frames as well as the logical packet’s opcode, and is then followed by the specified number of containers, which contain the data. The container count does not describe how many application objects can be deserialized from that data. Massive frames cannot be encrypted.
Every frame, including an encrypted frame, contains two additional bytes: a crc checksum and a cryptographically random count. The former is used to check for bitflips/modifications and the count to prevent replay attacks.
To read a frame from a byte-stream, you can use the SilkroadFrame::parse function to try and parse a frame from those bytes:
let (_, frame) = SilkroadFrame::parse(&[0x00, 0x00, 0x01, 0x00, 0x00, 0x00]).unwrap();
assert_eq!(
frame,
SilkroadFrame::Packet {
count: 0,
crc: 0,
opcode: 1,
data: Bytes::new(),
}
);This works vice versa, to write a frame into a byte stream, using SilkroadFrame::serialize:
let bytes = SilkroadFrame::Packet {
count: 0,
crc: 0,
opcode: 1,
data: Bytes::new(),
}
.serialize()
.expect("the frame is representable");
assert_eq!(bytes.as_ref(), &[0x00, 0x00, 0x01, 0x00, 0x00, 0x00]);Variants§
Packet
The most basic frame containing exactly one operation identified by its opcode.
Encrypted
A SilkroadFrame::Packet which is, however, still encrypted. This
contains the encrypted data and will first need to be decrypted (for
example, using the skrillax-security crate).
MassiveHeader
The header portion of a massive packet which contains information
that is necessary for the identification and usage of the following
SilkroadFrame::MassiveContainer frame(s). contained_count is the
number of following container frames, not a count of application
objects.
MassiveContainer
The data container portion of a massive packet. Must come after a SilkroadFrame::MassiveHeader and contributes data to the logical payload identified by that header.
Implementations§
Source§impl SilkroadFrame
impl SilkroadFrame
Sourcepub fn parse(data: &[u8]) -> Result<(usize, SilkroadFrame), FrameParseError>
pub fn parse(data: &[u8]) -> Result<(usize, SilkroadFrame), FrameParseError>
Tries to parse the first possible frame from the given data slice.
In addition to the created frame, it will also return the size of
consumed bytes by the frame. If not enough data is available, it
returns FrameParseError::Incomplete. The returned value in
FrameParseError::Incomplete is not guaranteed to be the exact
necessary bytes; it is possible to get it multiple times, even
when ensuring at least the amount of returned requested bytes were
added. This is because if we need the header info first and
only then can we know the expected size.
Complete but malformed frames return a structural parsing error.
Sourcepub fn from_data(data: &[u8]) -> Result<SilkroadFrame, FrameParseError>
pub fn from_data(data: &[u8]) -> Result<SilkroadFrame, FrameParseError>
Creates a SilkroadFrame given the received data. Generally, this will
result in a SilkroadFrame::Packet, unless we encounter a packet
with the opcode 0x600D, which is reserved for a massive packet,
consisting of a SilkroadFrame::MassiveHeader and multiple
SilkroadFrame::MassiveContainers.
The data must contain the two-byte opcode, one-byte security count, one-byte CRC, and any frame-specific fields. Malformed data is returned as a typed error.
Sourcepub fn content_size(&self) -> Result<FrameContentSize, FrameEncodeError>
pub fn content_size(&self) -> Result<FrameContentSize, FrameEncodeError>
Computes the value represented by the 15-bit length header field. Depending on the type of frame, this is either:
- The application payload size (basic frame)
- The unpadded application payload size, excluding the encrypted opcode, count, and CRC fields (encrypted frame)
- A fixed size (massive header frame)
- The mode byte and payload size (massive container frame)
Sourcepub fn validate(&self) -> Result<(), FrameEncodeError>
pub fn validate(&self) -> Result<(), FrameEncodeError>
Validates that this frame has a representable length and coherent shape.
Sourcepub fn packet_size(&self) -> Result<usize, FrameEncodeError>
pub fn packet_size(&self) -> Result<usize, FrameEncodeError>
Computes the total encoded size of this frame.
This includes the length header and, for encrypted frames, the fixed encrypted fields and block padding.
Sourcepub fn opcode(&self) -> Option<u16>
pub fn opcode(&self) -> Option<u16>
Tries to fetch the opcode of the frame, unless the packet is encrypted, which returns None.
Sourcepub fn serialize(&self) -> Result<Bytes, FrameEncodeError>
pub fn serialize(&self) -> Result<Bytes, FrameEncodeError>
Tries to serialize this frame into a byte stream.
Frames whose content length cannot be represented by the protocol’s 15-bit length field are rejected before any output is produced.