1use byteorder::{ByteOrder, LittleEndian};
15#[cfg(feature = "codec")]
16use bytes::Buf;
17use bytes::{BufMut, Bytes, BytesMut};
18use thiserror::Error;
19
20#[derive(Clone, Debug, Eq, Error, PartialEq)]
22#[non_exhaustive]
23pub enum FrameEncodeError {
24 #[error("frame content has {actual} bytes, but the maximum is {maximum}")]
26 ContentTooLarge { actual: usize, maximum: usize },
27 #[error(
30 "encrypted content size {content_size} requires {expected} encrypted bytes, but {actual} \
31 were provided"
32 )]
33 EncryptedDataLengthMismatch {
34 content_size: usize,
35 expected: usize,
36 actual: usize,
37 },
38}
39
40#[derive(Clone, Debug, Eq, Error, PartialEq)]
42pub enum FrameParseError {
43 #[error("frame requires at least {additional} additional byte(s)")]
45 Incomplete { additional: usize },
46 #[error("frame data requires at least 4 bytes, but only {actual} were provided")]
48 FrameTooShort { actual: usize },
49 #[error("massive frame is missing its mode byte")]
51 MissingMassiveMode,
52 #[error("invalid massive frame mode {mode}")]
54 InvalidMassiveMode { mode: u8 },
55 #[error("massive header requires at least 10 bytes, but only {actual} were provided")]
57 MassiveHeaderTooShort { actual: usize },
58}
59
60pub const MASSIVE_PACKET_OPCODE: u16 = 0x600D;
62pub const MASSIVE_HEADER_MODE: u8 = 1;
64pub const MASSIVE_CONTAINER_MODE: u8 = 0;
66const ENCRYPTED_FRAME_FLAG: u16 = 0x8000;
67const FRAME_CONTENT_LENGTH_MASK: u16 = 0x7FFF;
68const ENCRYPTED_ALIGNMENT: usize = 8;
69
70pub const MAX_FRAME_CONTENT_SIZE: usize = FRAME_CONTENT_LENGTH_MASK as usize;
73pub const MAX_MASSIVE_CONTAINER_INNER_SIZE: usize = MAX_FRAME_CONTENT_SIZE - 1;
76
77#[derive(Clone, Copy, Debug, Eq, PartialEq)]
79pub struct FrameContentSize(u16);
80
81impl TryFrom<usize> for FrameContentSize {
82 type Error = FrameEncodeError;
83
84 fn try_from(value: usize) -> Result<Self, Self::Error> {
85 if value > MAX_FRAME_CONTENT_SIZE {
86 return Err(FrameEncodeError::ContentTooLarge {
87 actual: value,
88 maximum: MAX_FRAME_CONTENT_SIZE,
89 });
90 }
91
92 let wire_value = u16::try_from(value).map_err(|_| FrameEncodeError::ContentTooLarge {
93 actual: value,
94 maximum: MAX_FRAME_CONTENT_SIZE,
95 })?;
96 Ok(Self(wire_value))
97 }
98}
99
100impl FrameContentSize {
101 pub const fn get(self) -> u16 {
103 self.0
104 }
105
106 pub const fn as_usize(self) -> usize {
108 self.0 as usize
109 }
110
111 pub const fn encrypted_wire_value(self) -> u16 {
113 self.0 | ENCRYPTED_FRAME_FLAG
114 }
115
116 pub fn encrypted_data_len(self) -> usize {
118 find_encrypted_length(self.as_usize() + 4)
119 }
120}
121
122fn find_encrypted_length(given_length: usize) -> usize {
128 let aligned_length = given_length % ENCRYPTED_ALIGNMENT;
129 if aligned_length == 0 {
130 return given_length;
132 }
133
134 given_length + (8 - aligned_length) }
136
137#[derive(Eq, PartialEq, Debug)]
190pub enum SilkroadFrame {
191 Packet {
194 count: u8,
195 crc: u8,
196 opcode: u16,
197 data: Bytes,
198 },
199 Encrypted {
203 content_size: usize,
204 encrypted_data: Bytes,
205 },
206 MassiveHeader {
212 count: u8,
213 crc: u8,
214 contained_opcode: u16,
215 contained_count: u16,
216 },
217 MassiveContainer { count: u8, crc: u8, inner: Bytes },
221}
222
223impl SilkroadFrame {
224 pub fn parse(data: &[u8]) -> Result<(usize, SilkroadFrame), FrameParseError> {
235 if data.len() < 2 {
236 return Err(FrameParseError::Incomplete {
237 additional: 2 - data.len(),
238 });
239 }
240
241 let length = LittleEndian::read_u16(&data[0..2]);
242 let encrypted = length & ENCRYPTED_FRAME_FLAG != 0;
243 let content_size = (length & FRAME_CONTENT_LENGTH_MASK) as usize;
244 let total_size = if encrypted {
245 find_encrypted_length(content_size + 4)
246 } else {
247 content_size + 4
248 };
249
250 let data = &data[2..];
251 if data.len() < total_size {
252 return Err(FrameParseError::Incomplete {
253 additional: total_size - data.len(),
254 });
255 }
256
257 let total_consumed = total_size + 2;
258 let data = Bytes::copy_from_slice(&data[0..total_size]);
259 if encrypted {
260 return Ok((
261 total_consumed,
262 SilkroadFrame::Encrypted {
263 content_size,
264 encrypted_data: data,
265 },
266 ));
267 }
268
269 Ok((total_consumed, Self::from_data(&data)?))
270 }
271
272 pub fn from_data(data: &[u8]) -> Result<SilkroadFrame, FrameParseError> {
282 if data.len() < 4 {
283 return Err(FrameParseError::FrameTooShort { actual: data.len() });
284 }
285
286 let opcode = LittleEndian::read_u16(&data[0..2]);
287 let count = data[2];
288 let crc = data[3];
289
290 if opcode == MASSIVE_PACKET_OPCODE {
291 let mode = *data.get(4).ok_or(FrameParseError::MissingMassiveMode)?;
292 match mode {
293 MASSIVE_HEADER_MODE => {
294 if data.len() < 10 {
295 return Err(FrameParseError::MassiveHeaderTooShort { actual: data.len() });
296 }
297 let inner_amount = LittleEndian::read_u16(&data[5..7]);
298 let inner_opcode = LittleEndian::read_u16(&data[7..9]);
299 Ok(SilkroadFrame::MassiveHeader {
300 count,
301 crc,
302 contained_opcode: inner_opcode,
303 contained_count: inner_amount,
304 })
305 },
306 MASSIVE_CONTAINER_MODE => Ok(SilkroadFrame::MassiveContainer {
307 count,
308 crc,
309 inner: Bytes::copy_from_slice(&data[5..]),
310 }),
311 mode => Err(FrameParseError::InvalidMassiveMode { mode }),
312 }
313 } else {
314 Ok(SilkroadFrame::Packet {
315 count,
316 crc,
317 opcode,
318 data: Bytes::copy_from_slice(&data[4..]),
319 })
320 }
321 }
322
323 pub fn content_size(&self) -> Result<FrameContentSize, FrameEncodeError> {
331 let size = match &self {
332 SilkroadFrame::Packet { data, .. } => data.len(),
333 SilkroadFrame::Encrypted { content_size, .. } => *content_size,
334 SilkroadFrame::MassiveHeader { .. } => {
335 6
339 },
340 SilkroadFrame::MassiveContainer { inner, .. } => {
341 inner
342 .len()
343 .checked_add(1)
344 .ok_or(FrameEncodeError::ContentTooLarge {
345 actual: usize::MAX,
346 maximum: MAX_FRAME_CONTENT_SIZE,
347 })?
348 },
349 };
350
351 FrameContentSize::try_from(size)
352 }
353
354 fn validated_content_size(&self) -> Result<FrameContentSize, FrameEncodeError> {
355 let content_size = self.content_size()?;
356 if let SilkroadFrame::Encrypted {
357 content_size: declared_content_size,
358 encrypted_data,
359 } = self
360 {
361 let expected = content_size.encrypted_data_len();
362 if encrypted_data.len() != expected {
363 return Err(FrameEncodeError::EncryptedDataLengthMismatch {
364 content_size: *declared_content_size,
365 expected,
366 actual: encrypted_data.len(),
367 });
368 }
369 }
370
371 Ok(content_size)
372 }
373
374 pub fn validate(&self) -> Result<(), FrameEncodeError> {
376 self.validated_content_size().map(|_| ())
377 }
378
379 pub fn packet_size(&self) -> Result<usize, FrameEncodeError> {
384 let content_size = self.validated_content_size()?;
385 Ok(match self {
386 SilkroadFrame::Encrypted { .. } => content_size.encrypted_data_len() + 2,
387 _ => 6 + content_size.as_usize(),
388 })
389 }
390
391 pub fn opcode(&self) -> Option<u16> {
394 match &self {
395 SilkroadFrame::Packet { opcode, .. } => Some(*opcode),
396 SilkroadFrame::Encrypted { .. } => None,
397 _ => Some(0x600D),
398 }
399 }
400
401 pub fn serialize(&self) -> Result<Bytes, FrameEncodeError> {
406 let content_size = self.validated_content_size()?;
407 let packet_size = match self {
408 SilkroadFrame::Encrypted { .. } => content_size.encrypted_data_len() + 2,
409 _ => 6 + content_size.as_usize(),
410 };
411 let mut output = BytesMut::with_capacity(packet_size);
412
413 match &self {
414 SilkroadFrame::Packet {
415 count,
416 crc,
417 opcode,
418 data,
419 } => {
420 output.put_u16_le(content_size.get());
421 output.put_u16_le(*opcode);
422 output.put_u8(*count);
423 output.put_u8(*crc);
424 output.put_slice(data);
425 },
426 SilkroadFrame::Encrypted { encrypted_data, .. } => {
427 output.put_u16_le(content_size.encrypted_wire_value());
428 output.put_slice(encrypted_data);
429 },
430 SilkroadFrame::MassiveHeader {
431 count,
432 crc,
433 contained_opcode,
434 contained_count,
435 } => {
436 output.put_u16_le(content_size.get());
437 output.put_u16_le(MASSIVE_PACKET_OPCODE);
438 output.put_u8(*count);
439 output.put_u8(*crc);
440 output.put_u8(MASSIVE_HEADER_MODE);
441 output.put_u16_le(*contained_count);
442 output.put_u16_le(*contained_opcode);
443 output.put_u8(0);
444 },
445 SilkroadFrame::MassiveContainer { count, crc, inner } => {
446 output.put_u16_le(content_size.get());
447 output.put_u16_le(MASSIVE_PACKET_OPCODE);
448 output.put_u8(*count);
449 output.put_u8(*crc);
450 output.put_u8(MASSIVE_CONTAINER_MODE);
451 output.put_slice(inner);
452 },
453 }
454
455 Ok(output.freeze())
456 }
457}
458
459#[cfg(feature = "codec")]
460pub use codec::*;
461
462#[cfg(feature = "codec")]
463mod codec {
464 use super::*;
465 use std::io;
466 use tokio_util::codec::{Decoder, Encoder};
467
468 pub struct SilkroadCodec;
474
475 impl Encoder<SilkroadFrame> for SilkroadCodec {
476 type Error = io::Error;
477
478 fn encode(&mut self, item: SilkroadFrame, dst: &mut BytesMut) -> Result<(), Self::Error> {
479 let bytes = item
480 .serialize()
481 .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error))?;
482 dst.extend_from_slice(&bytes);
483 Ok(())
484 }
485 }
486
487 impl Decoder for SilkroadCodec {
488 type Item = SilkroadFrame;
489 type Error = io::Error;
490
491 fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
492 match SilkroadFrame::parse(src) {
493 Ok((bytes_read, frame)) => {
494 src.advance(bytes_read);
495 Ok(Some(frame))
496 },
497 Err(FrameParseError::Incomplete { .. }) => Ok(None),
498 Err(error) => Err(io::Error::new(io::ErrorKind::InvalidData, error)),
499 }
500 }
501 }
502}
503
504#[cfg(test)]
505mod test {
506 #[cfg(feature = "codec")]
507 use crate::SilkroadCodec;
508 use crate::{FrameEncodeError, FrameParseError, MAX_FRAME_CONTENT_SIZE, SilkroadFrame};
509 use bytes::Bytes;
510 #[cfg(feature = "codec")]
511 use bytes::BytesMut;
512 #[cfg(feature = "codec")]
513 use tokio_util::codec::{Decoder, Encoder};
514
515 #[test]
516 fn test_parse_empty() {
517 let data = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
518 let (consumed, packet) =
519 SilkroadFrame::parse(&data).expect("Should parse empty, valid data");
520 assert_eq!(6, consumed);
521 assert_eq!(
522 SilkroadFrame::Packet {
523 count: 0,
524 crc: 0,
525 opcode: 0,
526 data: Bytes::new(),
527 },
528 packet
529 );
530 }
531
532 #[test]
533 fn test_parse_incomplete() {
534 assert_eq!(
535 Err(FrameParseError::Incomplete { additional: 2 }),
536 SilkroadFrame::parse(&[])
537 );
538 assert_eq!(
539 Err(FrameParseError::Incomplete { additional: 1 }),
540 SilkroadFrame::parse(&[0x00])
541 );
542
543 let data = [0x00, 0x00, 0x00, 0x00, 0x00];
544 let res = SilkroadFrame::parse(&data);
545 assert_eq!(Err(FrameParseError::Incomplete { additional: 1 }), res);
546
547 let data = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
548 let res = SilkroadFrame::parse(&data);
549 assert_eq!(Err(FrameParseError::Incomplete { additional: 1 }), res);
550 }
551
552 #[test]
553 fn test_parse_rejects_massive_frame_without_mode() {
554 let data = [0x00, 0x00, 0x0D, 0x60, 0x00, 0x00];
555
556 let result = SilkroadFrame::parse(&data);
557
558 assert_eq!(Err(FrameParseError::MissingMassiveMode), result);
559 }
560
561 #[test]
562 fn peer_massive_mode_outside_protocol_is_rejected() {
563 let wire = [0x02, 0x00, 0x0D, 0x60, 0x00, 0x00, 0x02, 0xAA];
566
567 let result = SilkroadFrame::parse(&wire);
568
569 assert_eq!(Err(FrameParseError::InvalidMassiveMode { mode: 2 }), result);
570 }
571
572 #[test]
573 fn test_parse_rejects_truncated_massive_header() {
574 for content_size in 1usize..6 {
575 let mut data = vec![content_size as u8, 0x00, 0x0D, 0x60, 0x00, 0x00, 0x01];
576 data.resize(6 + content_size, 0);
577
578 let result = SilkroadFrame::parse(&data);
579
580 assert_eq!(
581 Err(FrameParseError::MassiveHeaderTooShort {
582 actual: 4 + content_size,
583 }),
584 result
585 );
586 }
587 }
588
589 #[test]
590 fn test_from_data_rejects_short_frame() {
591 for actual in 0..4 {
592 let data = [0; 3];
593
594 assert_eq!(
595 Err(FrameParseError::FrameTooShort { actual }),
596 SilkroadFrame::from_data(&data[..actual])
597 );
598 }
599 }
600
601 #[test]
602 fn test_parse_content() {
603 let data = [0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01];
604 let (consumed, packet) = SilkroadFrame::parse(&data).expect("Should parse valid data");
605 assert_eq!(8, consumed);
606 assert_eq!(
607 SilkroadFrame::Packet {
608 count: 0,
609 crc: 0,
610 opcode: 0x0001,
611 data: Bytes::from_static(&[0x01, 0x01]),
612 },
613 packet
614 );
615 }
616
617 #[test]
618 fn test_parse_encrypted() {
619 let data = [0x02, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01];
620 let (consumed, packet) = SilkroadFrame::parse(&data).expect("Should parse valid data");
621 assert_eq!(10, consumed);
622 assert_eq!(
623 SilkroadFrame::Encrypted {
624 content_size: 2,
625 encrypted_data: Bytes::from_static(&[
626 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
627 ]),
628 },
629 packet
630 );
631 }
632
633 #[test]
634 fn test_parse_massive() {
635 let header = [
636 0x06, 0x00, 0x0D, 0x60, 0x00, 0x00, 0x01, 0x01, 0x00, 0x42, 0x00, 0x00,
637 ];
638 let (consumed, packet) = SilkroadFrame::parse(&header).expect("Should parse valid data");
639 assert_eq!(12, consumed);
640 assert_eq!(
641 SilkroadFrame::MassiveHeader {
642 count: 0,
643 crc: 0,
644 contained_opcode: 0x42,
645 contained_count: 1,
646 },
647 packet
648 );
649
650 let header = [0x02, 0x00, 0x0D, 0x60, 0x00, 0x00, 0x00, 0x01];
651 let (consumed, packet) = SilkroadFrame::parse(&header).expect("Should parse valid data");
652 assert_eq!(8, consumed);
653 assert_eq!(
654 SilkroadFrame::MassiveContainer {
655 count: 0,
656 crc: 0,
657 inner: Bytes::from_static(&[0x01]),
658 },
659 packet
660 );
661 }
662
663 #[test]
664 fn massive_header_count_round_trip_preserves_full_range() {
665 for contained_count in [0, u16::MAX] {
666 let frame = SilkroadFrame::MassiveHeader {
667 count: 0,
668 crc: 0,
669 contained_opcode: 0x42,
670 contained_count,
671 };
672 let wire = frame
673 .serialize()
674 .expect("a massive header should be representable");
675
676 let (consumed, parsed) =
677 SilkroadFrame::parse(&wire).expect("a massive header count should round-trip");
678
679 assert_eq!(wire.len(), consumed);
680 assert_eq!(frame, parsed);
681 }
682 }
683
684 #[cfg(feature = "codec")]
685 #[test]
686 fn test_decoder() {
687 let mut codec = SilkroadCodec;
688 let mut buffer = BytesMut::new();
689 buffer.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]);
690 let decoded = codec.decode(&mut buffer);
691 assert!(matches!(decoded, Ok(None)));
692
693 buffer.extend_from_slice(&[0x00, 0x00]);
694 let decoded = codec.decode_eof(&mut buffer).unwrap();
695 assert_eq!(
696 Some(SilkroadFrame::Packet {
697 count: 0,
698 crc: 0,
699 opcode: 0,
700 data: Bytes::new(),
701 }),
702 decoded
703 );
704 }
705
706 #[cfg(feature = "codec")]
707 #[test]
708 fn test_decoder_rejects_malformed_frame() {
709 let mut codec = SilkroadCodec;
710 let mut buffer = BytesMut::from(&[0x00, 0x00, 0x0D, 0x60, 0x00, 0x00][..]);
711
712 let error = codec
713 .decode(&mut buffer)
714 .expect_err("malformed frame should be rejected");
715
716 assert_eq!(std::io::ErrorKind::InvalidData, error.kind());
717 }
718
719 #[test]
720 fn test_serialize_empty() {
721 let data = SilkroadFrame::Packet {
722 count: 0,
723 crc: 0,
724 opcode: 0,
725 data: Bytes::new(),
726 }
727 .serialize()
728 .expect("an empty frame should be representable");
729 assert_eq!(data.as_ref(), &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
730 }
731
732 #[test]
733 fn maximum_plain_frame_length_round_trips_without_setting_encryption_flag() {
734 let frame = SilkroadFrame::Packet {
735 count: 7,
736 crc: 9,
737 opcode: 0x1234,
738 data: Bytes::from(vec![0xAB; MAX_FRAME_CONTENT_SIZE]),
739 };
740
741 let wire = frame
742 .serialize()
743 .expect("the maximum 15-bit content length is representable");
744 let (consumed, decoded) =
745 SilkroadFrame::parse(&wire).expect("the maximum plain frame should parse");
746
747 assert_eq!([0xFF, 0x7F], wire[..2]);
748 assert_eq!(wire.len(), consumed);
749 assert_eq!(frame, decoded);
750 }
751
752 #[test]
753 fn oversized_plain_frame_is_rejected() {
754 let frame = SilkroadFrame::Packet {
755 count: 0,
756 crc: 0,
757 opcode: 0x1234,
758 data: Bytes::from(vec![0; MAX_FRAME_CONTENT_SIZE + 1]),
759 };
760
761 let error = frame
762 .serialize()
763 .expect_err("a plain frame cannot use the encryption marker as length");
764
765 assert_eq!(
766 FrameEncodeError::ContentTooLarge {
767 actual: MAX_FRAME_CONTENT_SIZE + 1,
768 maximum: MAX_FRAME_CONTENT_SIZE,
769 },
770 error
771 );
772 }
773
774 #[cfg(feature = "codec")]
775 #[test]
776 fn codec_rejection_does_not_modify_the_destination_buffer() {
777 let frame = SilkroadFrame::Packet {
778 count: 0,
779 crc: 0,
780 opcode: 0x1234,
781 data: Bytes::from(vec![0; MAX_FRAME_CONTENT_SIZE + 1]),
782 };
783 let mut codec = SilkroadCodec;
784 let mut destination = BytesMut::from(&b"canary"[..]);
785
786 let error = codec
787 .encode(frame, &mut destination)
788 .expect_err("the codec must reject an unrepresentable frame");
789
790 assert_eq!(std::io::ErrorKind::InvalidInput, error.kind());
791 assert_eq!(&b"canary"[..], destination.as_ref());
792 }
793
794 #[test]
795 fn test_serialize_encrypted() {
796 let data = SilkroadFrame::Encrypted {
797 content_size: 0,
798 encrypted_data: Bytes::from_static(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
799 }
800 .serialize()
801 .expect("an empty encrypted frame should be representable");
802 assert_eq!(
803 data.as_ref(),
804 &[0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
805 );
806 }
807
808 #[test]
809 fn encrypted_frame_with_mismatched_ciphertext_length_is_rejected() {
810 for actual in [7, 16] {
811 let frame = SilkroadFrame::Encrypted {
812 content_size: 1,
813 encrypted_data: Bytes::from(vec![0; actual]),
814 };
815
816 let expected_error = FrameEncodeError::EncryptedDataLengthMismatch {
817 content_size: 1,
818 expected: 8,
819 actual,
820 };
821
822 assert_eq!(
823 expected_error,
824 frame
825 .packet_size()
826 .expect_err("an invalid frame has no encoded packet size")
827 );
828 assert_eq!(
829 expected_error,
830 frame
831 .serialize()
832 .expect_err("ciphertext must match the padded encrypted content length")
833 );
834 }
835 }
836
837 #[test]
838 fn oversized_encrypted_frame_is_rejected() {
839 let frame = SilkroadFrame::Encrypted {
840 content_size: MAX_FRAME_CONTENT_SIZE + 1,
841 encrypted_data: Bytes::new(),
842 };
843
844 let error = frame
845 .serialize()
846 .expect_err("encrypted content must fit the 15-bit length field");
847
848 assert_eq!(
849 FrameEncodeError::ContentTooLarge {
850 actual: MAX_FRAME_CONTENT_SIZE + 1,
851 maximum: MAX_FRAME_CONTENT_SIZE,
852 },
853 error
854 );
855 }
856
857 #[test]
858 fn test_serialize_massive() {
859 let data = SilkroadFrame::MassiveHeader {
860 count: 0,
861 crc: 0,
862 contained_opcode: 0x42,
863 contained_count: 1,
864 }
865 .serialize()
866 .expect("a massive header should be representable");
867 assert_eq!(
868 data.as_ref(),
869 &[
870 0x06, 0x00, 0x0D, 0x60, 0x00, 0x00, 0x01, 0x01, 0x00, 0x42, 0x00, 0x00
871 ]
872 );
873
874 let data = SilkroadFrame::MassiveContainer {
875 count: 0,
876 crc: 0,
877 inner: Bytes::new(),
878 }
879 .serialize()
880 .expect("an empty massive container should be representable");
881 assert_eq!(data.as_ref(), &[0x01, 0x00, 0x0D, 0x60, 0x00, 0x00, 0x00]);
882 }
883}