//! Low-level message building and reading, mirroring Soulseek.NET's //! `MessageBuilder` / `MessageReader`. use bytes::{Buf, BufMut, Bytes, BytesMut}; use flate2::read::ZlibDecoder; use std::io::Read; use crate::{ProtoError, Result}; /// Builds one outgoing message. `finish_*` prepends the length header. pub struct Writer { buf: BytesMut, } impl Default for Writer { fn default() -> Self { Self::new() } } impl Writer { pub fn new() -> Self { Writer { buf: BytesMut::with_capacity(64), } } pub fn u8(mut self, v: u8) -> Self { self.buf.put_u8(v); self } pub fn u32(mut self, v: u32) -> Self { self.buf.put_u32_le(v); self } pub fn u64(mut self, v: u64) -> Self { self.buf.put_u64_le(v); self } pub fn bool(self, v: bool) -> Self { self.u8(v as u8) } pub fn string(mut self, s: &str) -> Self { self.buf.put_u32_le(s.len() as u32); self.buf.put_slice(s.as_bytes()); self } /// Frame with a u32 code: `[len][code:u32][payload]`. pub fn finish_u32(self, code: u32) -> Vec { let mut out = Vec::with_capacity(self.buf.len() + 8); out.extend_from_slice(&((self.buf.len() as u32 + 4).to_le_bytes())); out.extend_from_slice(&code.to_le_bytes()); out.extend_from_slice(&self.buf); out } /// Frame with a u32 code and a zlib-compressed payload: /// `[len][code:u32][zlib(payload)]` (search responses, browse responses). pub fn finish_u32_compressed(self, code: u32) -> Vec { use flate2::write::ZlibEncoder; use flate2::Compression; use std::io::Write; let mut enc = ZlibEncoder::new(Vec::new(), Compression::default()); enc.write_all(&self.buf).expect("zlib write"); let compressed = enc.finish().expect("zlib finish"); let mut out = Vec::with_capacity(compressed.len() + 8); out.extend_from_slice(&((compressed.len() as u32 + 4).to_le_bytes())); out.extend_from_slice(&code.to_le_bytes()); out.extend_from_slice(&compressed); out } /// Frame with a single-byte code: `[len][code:u8][payload]` /// (PeerInit / PierceFirewall only). pub fn finish_u8(self, code: u8) -> Vec { let mut out = Vec::with_capacity(self.buf.len() + 5); out.extend_from_slice(&((self.buf.len() as u32 + 1).to_le_bytes())); out.push(code); out.extend_from_slice(&self.buf); out } } /// Reads one message payload (the bytes after the code). pub struct Reader { buf: Bytes, } impl Reader { pub fn new(payload: Bytes) -> Self { Reader { buf: payload } } pub fn remaining(&self) -> usize { self.buf.remaining() } pub fn has_more(&self) -> bool { self.buf.has_remaining() } /// Take all remaining bytes. pub fn rest(&mut self) -> Bytes { let n = self.buf.remaining(); self.buf.copy_to_bytes(n) } pub fn u8(&mut self) -> Result { if self.buf.remaining() < 1 { return Err(ProtoError::UnexpectedEof); } Ok(self.buf.get_u8()) } pub fn bool(&mut self) -> Result { Ok(self.u8()? != 0) } pub fn u32(&mut self) -> Result { if self.buf.remaining() < 4 { return Err(ProtoError::UnexpectedEof); } Ok(self.buf.get_u32_le()) } pub fn u64(&mut self) -> Result { if self.buf.remaining() < 8 { return Err(ProtoError::UnexpectedEof); } Ok(self.buf.get_u64_le()) } /// IPv4 address; wire order is reversed relative to display order. pub fn ipv4(&mut self) -> Result { if self.buf.remaining() < 4 { return Err(ProtoError::UnexpectedEof); } let mut b = [0u8; 4]; self.buf.copy_to_slice(&mut b); Ok(std::net::Ipv4Addr::new(b[3], b[2], b[1], b[0])) } /// UTF-8 with Latin-1 fallback, matching what real-world peers send. pub fn string(&mut self) -> Result { let len = self.u32()? as usize; if self.buf.remaining() < len { return Err(ProtoError::UnexpectedEof); } let bytes = self.buf.copy_to_bytes(len); Ok(match std::str::from_utf8(&bytes) { Ok(s) => s.to_owned(), Err(_) => bytes.iter().map(|&b| b as char).collect(), }) } /// A length-prefixed raw byte string (a user picture, for one): the same /// framing as `string`, without the UTF-8 interpretation. pub fn bytes(&mut self) -> Result { let len = self.u32()? as usize; if self.buf.remaining() < len { return Err(ProtoError::UnexpectedEof); } Ok(self.buf.copy_to_bytes(len)) } /// Replace the remaining payload with its zlib-decompressed form. pub fn decompress(&mut self) -> Result<()> { let mut out = Vec::new(); ZlibDecoder::new(&self.buf[..]).read_to_end(&mut out)?; if out.len() > crate::MAX_MESSAGE_SIZE { return Err(ProtoError::TooLarge(out.len())); } self.buf = Bytes::from(out); Ok(()) } } #[cfg(test)] mod tests { use super::*; #[test] fn roundtrip_primitives() { let msg = Writer::new().u32(7).string("héllo").u64(9).finish_u32(26); // len = 4 (code) + 4 + 4+6 + 8 = 26 assert_eq!(&msg[0..4], &26u32.to_le_bytes()); assert_eq!(&msg[4..8], &26u32.to_le_bytes()); let mut r = Reader::new(Bytes::copy_from_slice(&msg[8..])); assert_eq!(r.u32().unwrap(), 7); assert_eq!(r.string().unwrap(), "héllo"); assert_eq!(r.u64().unwrap(), 9); assert!(!r.has_more()); } #[test] fn u8_code_frame() { let msg = Writer::new().u32(123).finish_u8(0); assert_eq!(&msg[0..4], &5u32.to_le_bytes()); assert_eq!(msg[4], 0); } #[test] fn latin1_fallback() { let mut raw = Vec::new(); raw.extend_from_slice(&2u32.to_le_bytes()); raw.extend_from_slice(&[0xE9, 0x21]); // "é!" in Latin-1, invalid UTF-8 let mut r = Reader::new(Bytes::from(raw)); assert_eq!(r.string().unwrap(), "é!"); } }