use tokio::net::TcpStream;
use super::{
noise::NOISE_PREAMBLE,
stream_reader::{StreamDecoder, StreamReader},
stream_writer::{StreamEncoder, StreamWriter},
StreamPair,
};
use crate::error::{ClientError, ConnectionError, ProtocolError, StreamError};
pub(super) const PLAIN_PREAMBLE: u8 = 0x00;
pub(crate) async fn connect(addr: &str) -> Result<StreamPair, ClientError> {
let (read_stream, write_stream) = TcpStream::connect(addr)
.await
.map_err(|e| ConnectionError::TcpConnect {
address: addr.to_owned(),
source: e,
})?
.into_split();
tracing::debug!("Tcp connection established to {addr}");
Ok((
StreamReader::new(read_stream).with_decoder(Box::new(PlainDecoder)),
StreamWriter::new(write_stream).with_encoder(Box::new(PlainEncoder)),
))
}
#[derive(Debug)]
struct PlainDecoder;
impl StreamDecoder for PlainDecoder {
fn decode(&self, buffer: &mut Vec<u8>) -> Result<Option<Vec<u8>>, ClientError> {
read_frame_from_buffer(buffer)
}
}
#[derive(Debug)]
struct PlainEncoder;
impl StreamEncoder for PlainEncoder {
fn encode(&self, payload: Vec<u8>) -> Result<Vec<u8>, ClientError> {
create_frame(&payload)
}
}
fn create_frame(payload: &[u8]) -> Result<Vec<u8>, ClientError> {
if payload.len() < 4 {
return Err(StreamError::InvalidFrame {
reason: "Payload must be at least 4 bytes long".to_owned(),
}
.into());
}
let type_id = u16::from_be_bytes([payload[0], payload[1]]);
let frame_len = u16::from_be_bytes([payload[2], payload[3]]);
Ok([
vec![PLAIN_PREAMBLE],
convert_to_leb128(frame_len),
convert_to_leb128(type_id),
payload[4..].to_vec(),
]
.concat())
}
fn read_frame_from_buffer(buffer: &mut Vec<u8>) -> Result<Option<Vec<u8>>, ClientError> {
if buffer.len() < 3 {
return Ok(None);
}
let preamble = buffer[0];
match preamble {
PLAIN_PREAMBLE => {}
NOISE_PREAMBLE => {
return Err(ProtocolError::UnexpectedEncryption.into());
}
_ => {
return Err(StreamError::InvalidFrame {
reason: format!("Invalid preamble: {preamble}"),
}
.into());
}
}
let (frame_len, next_index) = match convert_from_leb128(buffer, 1) {
Some((len, index)) => (usize::from(len), index),
None => return Ok(None),
};
let Some((type_id, next_index)) = convert_from_leb128(buffer, next_index) else {
return Ok(None);
};
if buffer.len() < next_index + frame_len {
tracing::debug!(
"Waiting for more data, expected {} bytes, got {}",
frame_len,
buffer.len()
);
return Ok(None);
}
let frame = buffer
.drain(..frame_len + next_index)
.skip(next_index)
.collect();
let frame_len = u16::try_from(frame_len).map_err(|_e| StreamError::FrameTooLarge {
size: frame_len,
#[allow(clippy::as_conversions, reason = "u16:MAX should always fit in usize")]
max_size: u16::MAX as usize,
})?;
Ok(Some(
[
type_id.to_be_bytes().to_vec(),
frame_len.to_be_bytes().to_vec(),
frame,
]
.concat(),
))
}
fn convert_to_leb128(mut value: u16) -> Vec<u8> {
if value <= 0x7F {
return vec![u8::try_from(value).expect("u8")];
}
let mut result = Vec::new();
while value != 0 {
let mut temp = u8::try_from(value & 0x7F).expect("u8");
value >>= 7;
if value != 0 {
temp |= 0x80;
}
result.push(temp);
}
result
}
fn convert_from_leb128(payload: &[u8], start_pos: usize) -> Option<(u16, usize)> {
let mut result: u16 = 0;
let mut shift = 0;
for (index, byte) in payload.iter().enumerate().skip(start_pos) {
let value = u16::from(byte & 0x7F);
result |= value << shift;
if byte & 0x80 == 0 {
return Some((result, index + 1));
}
shift += 7;
if shift >= 16 {
return None;
}
}
None }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_convert_to_leb128_and_from_leb128() {
let values = [0u16, 1, 127, 128, 255, 300, 16383, 16384, u16::MAX];
for &val in &values {
let leb = convert_to_leb128(val);
let (decoded, next_index) = convert_from_leb128(&leb, 0).expect("Should decode");
assert_eq!(decoded, val);
assert_eq!(next_index, leb.len());
}
}
#[test]
fn test_create_frame_and_read_frame_from_buffer() {
let type_id: u16 = 0x1234;
let payload_data = vec![1, 2, 3, 4, 5, 6];
let frame_len = u16::try_from(payload_data.len()).expect("payload too large");
let mut payload = Vec::new();
payload.extend_from_slice(&type_id.to_be_bytes());
payload.extend_from_slice(&frame_len.to_be_bytes());
payload.extend_from_slice(&payload_data);
let mut buffer = create_frame(&payload).expect("Frame should be created");
let decoded = read_frame_from_buffer(&mut buffer)
.expect("Should decode")
.expect("Should have frame");
assert_eq!(&decoded[0..2], &type_id.to_be_bytes());
assert_eq!(&decoded[2..4], &frame_len.to_be_bytes());
assert_eq!(&decoded[4..], &payload_data);
assert!(buffer.is_empty());
}
#[test]
fn test_create_frame_with_short_payload() {
let payload = vec![1, 2, 3]; let result = create_frame(&payload);
result.unwrap_err();
}
#[test]
fn test_read_frame_from_buffer_with_noise_preamble() {
let mut buffer = vec![NOISE_PREAMBLE, 0x01, 0x02, 0x03];
let result = read_frame_from_buffer(&mut buffer);
result.unwrap_err();
}
#[test]
fn test_read_frame_from_buffer_with_invalid_preamble() {
let mut buffer = vec![0xFF, 0x01, 0x02, 0x03];
let result = read_frame_from_buffer(&mut buffer);
result.unwrap_err();
}
#[test]
fn test_read_frame_from_buffer_incomplete_leb128() {
let mut buffer = vec![PLAIN_PREAMBLE, 0x81];
let result = read_frame_from_buffer(&mut buffer);
assert!(result.unwrap().is_none());
}
#[test]
fn test_read_frame_from_buffer_waits_for_more_data() {
let type_id: u16 = 0x1234;
let frame_len: u16 = 10;
let mut frame = vec![PLAIN_PREAMBLE];
frame.extend(convert_to_leb128(frame_len));
frame.extend(convert_to_leb128(type_id));
frame.extend(vec![0u8; 5]);
let mut buffer = frame;
let result = read_frame_from_buffer(&mut buffer);
assert!(result.unwrap().is_none());
}
}