use crate::{error::ProtocolError, proto::parser::Response};
const LOG_TARGET: &str = "yosemite::proto::router-api";
#[derive(Debug)]
enum RouterApiControllerState {
Uninitialized,
Handshaking,
Handshaked,
AwaitingLookupResponse,
AwaitingDestinationResponse,
LookupSucceeded {
destination: String,
},
DestinationGenerated {
destination: String,
private_key: String,
},
Poisoned,
}
pub struct RouterApiController {
state: RouterApiControllerState,
}
impl RouterApiController {
pub fn new() -> Self {
Self {
state: RouterApiControllerState::Uninitialized,
}
}
pub fn handshake_router_api(&mut self) -> Result<Vec<u8>, ProtocolError> {
match std::mem::replace(&mut self.state, RouterApiControllerState::Poisoned) {
RouterApiControllerState::Uninitialized => {
tracing::trace!(
target: LOG_TARGET,
"send handshake for router api",
);
self.state = RouterApiControllerState::Handshaking;
Ok(String::from("HELLO VERSION\n").into_bytes())
}
state => {
tracing::warn!(
target: LOG_TARGET,
?state,
"cannot handshake router api, invalid state",
);
debug_assert!(false);
Err(ProtocolError::InvalidState)
}
}
}
pub fn lookup_name(&mut self, name: &str) -> Result<Vec<u8>, ProtocolError> {
match std::mem::replace(&mut self.state, RouterApiControllerState::Poisoned) {
RouterApiControllerState::Handshaked => {
tracing::info!(
target: LOG_TARGET,
%name,
"lookup destination",
);
self.state = RouterApiControllerState::AwaitingLookupResponse;
Ok(format!("NAMING LOOKUP NAME={name}\n").into_bytes())
}
state => {
tracing::warn!(
target: LOG_TARGET,
?state,
"cannot lookup hostname, invalid state",
);
debug_assert!(false);
Err(ProtocolError::InvalidState)
}
}
}
pub fn generate_destination(&mut self) -> Result<Vec<u8>, ProtocolError> {
match std::mem::replace(&mut self.state, RouterApiControllerState::Poisoned) {
RouterApiControllerState::Handshaked => {
tracing::info!(
target: LOG_TARGET,
"generate destination",
);
self.state = RouterApiControllerState::AwaitingDestinationResponse;
Ok(format!("DEST GENERATE SIGNATURE_TYPE=7\n").into_bytes())
}
state => {
tracing::warn!(
target: LOG_TARGET,
?state,
"cannot generate destination, invalid state",
);
debug_assert!(false);
Err(ProtocolError::InvalidState)
}
}
}
pub fn handle_response(&mut self, response: &str) -> Result<(), ProtocolError> {
match std::mem::replace(&mut self.state, RouterApiControllerState::Poisoned) {
RouterApiControllerState::Handshaking => match Response::parse(response) {
Some(Response::Hello {
version: Ok(version),
}) => {
tracing::trace!(
target: LOG_TARGET,
%version,
"router api handshake done",
);
self.state = RouterApiControllerState::Handshaked;
Ok(())
}
Some(Response::Hello {
version: Err(error),
}) => return Err(ProtocolError::Router(error)),
None => {
tracing::warn!(
target: LOG_TARGET,
?response,
"invalid response from router for `HELLO`",
);
return Err(ProtocolError::InvalidMessage);
}
Some(response) => {
tracing::warn!(
target: LOG_TARGET,
?response,
"unexpected response from router for `HELLO`",
);
return Err(ProtocolError::InvalidState);
}
},
RouterApiControllerState::AwaitingLookupResponse => match Response::parse(response) {
Some(Response::NamingLookup {
result: Ok(destination),
}) => {
tracing::trace!(
target: LOG_TARGET,
"destination found",
);
self.state = RouterApiControllerState::LookupSucceeded { destination };
Ok(())
}
Some(Response::NamingLookup { result: Err(error) }) =>
return Err(ProtocolError::Router(error)),
None => {
tracing::warn!(
target: LOG_TARGET,
?response,
"invalid response from router for `NAMING LOOKUP`",
);
return Err(ProtocolError::InvalidMessage);
}
Some(response) => {
tracing::warn!(
target: LOG_TARGET,
?response,
"unexpected response from router for `NAMING LOOKUP`",
);
return Err(ProtocolError::InvalidState);
}
},
RouterApiControllerState::AwaitingDestinationResponse =>
match Response::parse(response) {
Some(Response::DestinationGeneration {
destination,
private_key,
}) => {
tracing::trace!(
target: LOG_TARGET,
"destination generated",
);
self.state = RouterApiControllerState::DestinationGenerated {
destination,
private_key,
};
Ok(())
}
None => {
tracing::warn!(
target: LOG_TARGET,
?response,
"invalid response from router for `DEST GENERATE`",
);
return Err(ProtocolError::InvalidMessage);
}
Some(response) => {
tracing::warn!(
target: LOG_TARGET,
?response,
"unexpected response from router for `DEST GENERATE`",
);
return Err(ProtocolError::InvalidState);
}
},
state => {
tracing::warn!(
target: LOG_TARGET,
?state,
"cannot handle response, invalid state",
);
debug_assert!(false);
Err(ProtocolError::InvalidState)
}
}
}
pub fn destination(&mut self) -> String {
match std::mem::replace(&mut self.state, RouterApiControllerState::Uninitialized) {
RouterApiControllerState::LookupSucceeded { destination } => destination,
_ => panic!("invalid state"),
}
}
pub fn generated_destination(&mut self) -> (String, String) {
match std::mem::replace(&mut self.state, RouterApiControllerState::Uninitialized) {
RouterApiControllerState::DestinationGenerated {
destination,
private_key,
} => (destination, private_key),
_ => panic!("invalid state"),
}
}
}