401 lines
15 KiB
Rust
401 lines
15 KiB
Rust
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// all variable size data is always prefixed with u64 length
|
|
// tags are u8
|
|
|
|
use crate::error::{self, ErrorKind};
|
|
use crate::text::ServerResponseText;
|
|
use nym_sphinx::addressing::clients::Recipient;
|
|
use nym_sphinx::anonymous_replies::requests::{AnonymousSenderTag, SENDER_TAG_SIZE};
|
|
use nym_sphinx::receiver::ReconstructedMessage;
|
|
|
|
use std::mem::size_of;
|
|
|
|
#[repr(u8)]
|
|
enum ServerResponseTag {
|
|
/// Value tag representing [`Error`] variant of the [`ServerResponse`]
|
|
Error = 0x00,
|
|
|
|
/// Value tag representing [`Received`] variant of the [`ServerResponse`]
|
|
Received = 0x01,
|
|
|
|
/// Value tag representing [`SelfAddress`] variant of the [`ServerResponse`]
|
|
SelfAddress = 0x02,
|
|
|
|
/// Value tag representing [`LaneQueueLength`] variant of the [`ServerResponse`]
|
|
LaneQueueLength = 0x03,
|
|
}
|
|
|
|
impl TryFrom<u8> for ServerResponseTag {
|
|
type Error = error::Error;
|
|
|
|
fn try_from(value: u8) -> Result<Self, error::Error> {
|
|
match value {
|
|
_ if value == (Self::Error as u8) => Ok(Self::Error),
|
|
_ if value == (Self::Received as u8) => Ok(Self::Received),
|
|
_ if value == (Self::SelfAddress as u8) => Ok(Self::SelfAddress),
|
|
_ if value == (Self::LaneQueueLength as u8) => Ok(Self::LaneQueueLength),
|
|
n => Err(error::Error::new(
|
|
ErrorKind::UnknownResponse,
|
|
format!("{n} does not correspond to any valid response tag"),
|
|
)),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum ServerResponse {
|
|
Received(ReconstructedMessage),
|
|
SelfAddress(Box<Recipient>),
|
|
LaneQueueLength { lane: u64, queue_length: usize },
|
|
Error(error::Error),
|
|
}
|
|
|
|
impl ServerResponse {
|
|
pub fn new_error<S: Into<String>>(message: S) -> Self {
|
|
ServerResponse::Error(error::Error {
|
|
kind: ErrorKind::Other,
|
|
message: message.into(),
|
|
})
|
|
}
|
|
|
|
// RECEIVED_RESPONSE_TAG || 1 | 0 indicating sender_tag || Option<sender_tag> || msg_len || msg
|
|
fn serialize_received(reconstructed_message: ReconstructedMessage) -> Vec<u8> {
|
|
let message_len_bytes = (reconstructed_message.message.len() as u64).to_be_bytes();
|
|
|
|
if let Some(sender_tag) = reconstructed_message.sender_tag {
|
|
std::iter::once(ServerResponseTag::Received as u8)
|
|
.chain(std::iter::once(true as u8))
|
|
.chain(sender_tag.to_bytes())
|
|
.chain(message_len_bytes.iter().cloned())
|
|
.chain(reconstructed_message.message)
|
|
.collect()
|
|
} else {
|
|
std::iter::once(ServerResponseTag::Received as u8)
|
|
.chain(std::iter::once(false as u8))
|
|
.chain(message_len_bytes.iter().cloned())
|
|
.chain(reconstructed_message.message)
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
// RECEIVED_RESPONSE_TAG || 1 | 0 indicating sender_tag || Option<sender_tag> || msg_len || msg
|
|
fn deserialize_received(b: &[u8]) -> Result<Self, error::Error> {
|
|
// this MUST match because it was called by 'deserialize'
|
|
|
|
// we must be able to read at the very least if it has a reply_surb and length of some field
|
|
if b.len() < 2 + size_of::<u64>() {
|
|
return Err(error::Error::new(
|
|
ErrorKind::TooShortResponse,
|
|
"not enough data provided to recover 'received'".to_string(),
|
|
));
|
|
}
|
|
debug_assert_eq!(b[0], ServerResponseTag::Received as u8);
|
|
|
|
let has_sender_tag = match b[1] {
|
|
0 => false,
|
|
1 => true,
|
|
n => {
|
|
return Err(error::Error::new(
|
|
ErrorKind::MalformedResponse,
|
|
format!("invalid sender tag flag {n}"),
|
|
))
|
|
}
|
|
};
|
|
|
|
let mut i = 2;
|
|
let sender_tag = if has_sender_tag {
|
|
if b[2..].len() < SENDER_TAG_SIZE {
|
|
return Err(error::Error::new(
|
|
ErrorKind::TooShortResponse,
|
|
"not enough data provided to recover 'received'".to_string(),
|
|
));
|
|
}
|
|
i += SENDER_TAG_SIZE;
|
|
Some(AnonymousSenderTag::from_bytes(
|
|
b[2..2 + SENDER_TAG_SIZE].try_into().unwrap(),
|
|
))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
if b[i..].len() < size_of::<u64>() {
|
|
return Err(error::Error::new(
|
|
ErrorKind::TooShortResponse,
|
|
"not enough data provided to recover 'received'".to_string(),
|
|
));
|
|
}
|
|
|
|
let message_len = u64::from_be_bytes(b[i..i + size_of::<u64>()].try_into().unwrap());
|
|
let message = &b[i + size_of::<u64>()..];
|
|
if message.len() as u64 != message_len {
|
|
return Err(error::Error::new(
|
|
ErrorKind::MalformedResponse,
|
|
format!(
|
|
"message len has inconsistent length. specified: {} got: {}",
|
|
message_len,
|
|
message.len()
|
|
),
|
|
));
|
|
}
|
|
|
|
Ok(ServerResponse::Received(ReconstructedMessage {
|
|
message: message.to_vec(),
|
|
sender_tag,
|
|
}))
|
|
}
|
|
|
|
// SELF_ADDRESS_RESPONSE_TAG || self_address
|
|
fn serialize_self_address(address: Recipient) -> Vec<u8> {
|
|
std::iter::once(ServerResponseTag::SelfAddress as u8)
|
|
.chain(address.to_bytes())
|
|
.collect()
|
|
}
|
|
|
|
// SELF_ADDRESS_RESPONSE_TAG || self_address
|
|
fn deserialize_self_address(b: &[u8]) -> Result<Self, error::Error> {
|
|
if b.len() != 1 + Recipient::LEN {
|
|
return Err(error::Error::new(
|
|
ErrorKind::TooShortResponse,
|
|
"not enough data provided to recover 'self_address'".to_string(),
|
|
));
|
|
}
|
|
|
|
// this MUST match because it was called by 'deserialize'
|
|
debug_assert_eq!(b[0], ServerResponseTag::SelfAddress as u8);
|
|
|
|
let mut recipient_bytes = [0u8; Recipient::LEN];
|
|
recipient_bytes.copy_from_slice(&b[1..1 + Recipient::LEN]);
|
|
|
|
let recipient = match Recipient::try_from_bytes(recipient_bytes) {
|
|
Ok(recipient) => recipient,
|
|
Err(err) => {
|
|
return Err(error::Error::new(
|
|
ErrorKind::MalformedResponse,
|
|
format!("malformed Recipient: {err}"),
|
|
))
|
|
}
|
|
};
|
|
|
|
Ok(ServerResponse::SelfAddress(Box::new(recipient)))
|
|
}
|
|
|
|
// LANE_QUEUE_LENGTH_RESPONSE_TAG || lane || queue_length
|
|
fn serialize_lane_queue_length(lane: u64, queue_length: usize) -> Vec<u8> {
|
|
std::iter::once(ServerResponseTag::LaneQueueLength as u8)
|
|
.chain(lane.to_be_bytes().iter().cloned())
|
|
.chain(queue_length.to_be_bytes().iter().cloned())
|
|
.collect()
|
|
}
|
|
|
|
// LANE_QUEUE_LENGTH_RESPONSE_TAG || lane || queue_length
|
|
fn deserialize_lane_queue_length(b: &[u8]) -> Result<Self, error::Error> {
|
|
// this MUST match because it was called by 'deserialize'
|
|
debug_assert_eq!(b[0], ServerResponseTag::LaneQueueLength as u8);
|
|
|
|
let mut lane_bytes = [0u8; size_of::<u64>()];
|
|
lane_bytes.copy_from_slice(&b[1..=size_of::<u64>()]);
|
|
let lane = u64::from_be_bytes(lane_bytes);
|
|
|
|
let mut queue_length_bytes = [0u8; size_of::<usize>()];
|
|
queue_length_bytes
|
|
.copy_from_slice(&b[1 + size_of::<u64>()..1 + size_of::<u64>() + size_of::<usize>()]);
|
|
let queue_length = usize::from_be_bytes(queue_length_bytes);
|
|
|
|
Ok(ServerResponse::LaneQueueLength { lane, queue_length })
|
|
}
|
|
|
|
// ERROR_RESPONSE_TAG || err_code || msg_len || msg
|
|
fn serialize_error(error: error::Error) -> Vec<u8> {
|
|
let message_len_bytes = (error.message.len() as u64).to_be_bytes();
|
|
std::iter::once(ServerResponseTag::Error as u8)
|
|
.chain(std::iter::once(error.kind as u8))
|
|
.chain(message_len_bytes)
|
|
.chain(error.message.into_bytes())
|
|
.collect()
|
|
}
|
|
|
|
// ERROR_RESPONSE_TAG || err_code || msg_len || msg
|
|
fn deserialize_error(b: &[u8]) -> Result<Self, error::Error> {
|
|
// this MUST match because it was called by 'deserialize'
|
|
debug_assert_eq!(b[0], ServerResponseTag::Error as u8);
|
|
|
|
if b.len() < size_of::<u8>() + size_of::<u64>() {
|
|
return Err(error::Error::new(
|
|
ErrorKind::TooShortResponse,
|
|
"not enough data provided to recover 'error'".to_string(),
|
|
));
|
|
}
|
|
|
|
let error_kind = ErrorKind::try_from(b[1])?;
|
|
|
|
let message_len = u64::from_be_bytes(b[2..2 + size_of::<u64>()].try_into().unwrap());
|
|
let message = &b[2 + size_of::<u64>()..];
|
|
if message.len() as u64 != message_len {
|
|
return Err(error::Error::new(
|
|
ErrorKind::MalformedResponse,
|
|
format!(
|
|
"message len has inconsistent length. specified: {} got: {}",
|
|
message_len,
|
|
message.len()
|
|
),
|
|
));
|
|
}
|
|
|
|
let err_message = match String::from_utf8(message.to_vec()) {
|
|
Ok(msg) => msg,
|
|
Err(err) => {
|
|
return Err(error::Error::new(
|
|
ErrorKind::MalformedResponse,
|
|
format!("malformed error message: {err}"),
|
|
))
|
|
}
|
|
};
|
|
|
|
Ok(ServerResponse::Error(error::Error::new(
|
|
error_kind,
|
|
err_message,
|
|
)))
|
|
}
|
|
|
|
pub fn serialize(self) -> Vec<u8> {
|
|
match self {
|
|
ServerResponse::Received(reconstructed_message) => {
|
|
Self::serialize_received(reconstructed_message)
|
|
}
|
|
ServerResponse::SelfAddress(address) => Self::serialize_self_address(*address),
|
|
ServerResponse::LaneQueueLength { lane, queue_length } => {
|
|
Self::serialize_lane_queue_length(lane, queue_length)
|
|
}
|
|
ServerResponse::Error(err) => Self::serialize_error(err),
|
|
}
|
|
}
|
|
|
|
pub fn deserialize(b: &[u8]) -> Result<Self, error::Error> {
|
|
if b.is_empty() {
|
|
// technically I'm not even sure this can ever be returned, because reading empty
|
|
// request would imply closed socket, but let's include it for completion sake
|
|
return Err(error::Error::new(
|
|
ErrorKind::EmptyResponse,
|
|
"no data provided".to_string(),
|
|
));
|
|
}
|
|
|
|
if b.len() < size_of::<u8>() {
|
|
return Err(error::Error::new(
|
|
ErrorKind::TooShortResponse,
|
|
format!(
|
|
"not enough data provided to recover response tag. Provided only {} bytes",
|
|
b.len()
|
|
),
|
|
));
|
|
}
|
|
|
|
let response_tag = ServerResponseTag::try_from(b[0])?;
|
|
|
|
// determine what kind of response that is and try to deserialize it
|
|
match response_tag {
|
|
ServerResponseTag::Received => Self::deserialize_received(b),
|
|
ServerResponseTag::SelfAddress => Self::deserialize_self_address(b),
|
|
ServerResponseTag::LaneQueueLength => Self::deserialize_lane_queue_length(b),
|
|
ServerResponseTag::Error => Self::deserialize_error(b),
|
|
}
|
|
}
|
|
|
|
pub fn into_binary(self) -> Vec<u8> {
|
|
self.serialize()
|
|
}
|
|
|
|
pub fn into_text(self) -> String {
|
|
// use the intermediate string structure and let serde do bunch of work for us
|
|
let text_resp = ServerResponseText::from(self);
|
|
|
|
text_resp.into()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn received_response_serialization_works() {
|
|
let received_with_sender_tag = ServerResponse::Received(ReconstructedMessage {
|
|
message: b"foomp".to_vec(),
|
|
sender_tag: Some([42u8; SENDER_TAG_SIZE].into()),
|
|
});
|
|
let bytes = received_with_sender_tag.serialize();
|
|
let recovered = ServerResponse::deserialize(&bytes).unwrap();
|
|
match recovered {
|
|
ServerResponse::Received(reconstructed) => {
|
|
assert_eq!(reconstructed.message, b"foomp".to_vec());
|
|
assert_eq!(
|
|
reconstructed.sender_tag,
|
|
Some([42u8; SENDER_TAG_SIZE].into())
|
|
)
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
|
|
let received_without_sender_tag = ServerResponse::Received(ReconstructedMessage {
|
|
message: b"foomp".to_vec(),
|
|
sender_tag: None,
|
|
});
|
|
let bytes = received_without_sender_tag.serialize();
|
|
let recovered = ServerResponse::deserialize(&bytes).unwrap();
|
|
match recovered {
|
|
ServerResponse::Received(reconstructed) => {
|
|
assert_eq!(reconstructed.message, b"foomp".to_vec());
|
|
assert!(reconstructed.sender_tag.is_none())
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn self_address_response_serialization_works() {
|
|
let recipient = Recipient::try_from_base58_string("CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f").unwrap();
|
|
let recipient_string = recipient.to_string();
|
|
|
|
let self_address_response = ServerResponse::SelfAddress(Box::new(recipient));
|
|
let bytes = self_address_response.serialize();
|
|
let recovered = ServerResponse::deserialize(&bytes).unwrap();
|
|
match recovered {
|
|
ServerResponse::SelfAddress(recipient) => {
|
|
assert_eq!(recipient.to_string(), recipient_string)
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn lane_queue_length_response_serialization_works() {
|
|
let lane_queue_length_response = ServerResponse::LaneQueueLength {
|
|
lane: 13,
|
|
queue_length: 42,
|
|
};
|
|
let bytes = lane_queue_length_response.serialize();
|
|
let recovered = ServerResponse::deserialize(&bytes).unwrap();
|
|
match recovered {
|
|
ServerResponse::LaneQueueLength { lane, queue_length } => {
|
|
assert_eq!(lane, 13);
|
|
assert_eq!(queue_length, 42)
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn error_response_serialization_works() {
|
|
let dummy_error = error::Error::new(ErrorKind::UnknownRequest, "foomp message".to_string());
|
|
let error_response = ServerResponse::Error(dummy_error.clone());
|
|
let bytes = error_response.serialize();
|
|
let recovered = ServerResponse::deserialize(&bytes).unwrap();
|
|
match recovered {
|
|
ServerResponse::Error(error) => assert_eq!(error, dummy_error),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|