2026-04-24 14:25:35 -06:00
|
|
|
//! Hook-state transitions and route helpers.
|
|
|
|
|
|
2026-04-25 11:46:45 -06:00
|
|
|
use alloc::string::String;
|
2026-04-24 14:25:35 -06:00
|
|
|
|
2026-04-24 14:27:55 -06:00
|
|
|
use crate::protocol::{
|
|
|
|
|
DataMessage, FaultMessage, PacketHeader, PacketType, ProtocolFault, encode_packet,
|
|
|
|
|
};
|
2026-04-24 14:25:35 -06:00
|
|
|
|
2026-04-25 12:15:38 -06:00
|
|
|
use super::super::{HookKey, RouteDecision};
|
2026-04-24 14:27:55 -06:00
|
|
|
use super::core::{EndpointError, EndpointOutcome, Ingress, LocalEvent, ProtocolEndpoint};
|
2026-04-24 14:25:35 -06:00
|
|
|
|
|
|
|
|
impl ProtocolEndpoint {
|
|
|
|
|
pub(crate) fn emit_fault_if_possible(
|
|
|
|
|
&mut self,
|
|
|
|
|
key: Option<HookKey>,
|
|
|
|
|
fault: ProtocolFault,
|
|
|
|
|
) -> Result<EndpointOutcome, EndpointError> {
|
|
|
|
|
let Some(key) = key else {
|
2026-04-25 20:47:37 -06:00
|
|
|
return Ok(EndpointOutcome::Dropped);
|
2026-04-24 14:25:35 -06:00
|
|
|
};
|
|
|
|
|
|
2026-04-25 12:15:38 -06:00
|
|
|
self.hooks.remove_pending(&key);
|
2026-04-24 14:25:35 -06:00
|
|
|
self.hooks.remove_active(&key);
|
|
|
|
|
|
|
|
|
|
let header = PacketHeader {
|
|
|
|
|
packet_type: PacketType::Fault,
|
|
|
|
|
src_path: self.path.clone(),
|
|
|
|
|
dst_path: key.return_path.clone(),
|
|
|
|
|
dst_leaf: None,
|
|
|
|
|
hook_id: Some(key.hook_id),
|
|
|
|
|
};
|
|
|
|
|
let message = FaultMessage { fault };
|
|
|
|
|
|
2026-04-25 12:37:54 -06:00
|
|
|
match self.decide_route(&key.return_path) {
|
2026-04-25 20:47:37 -06:00
|
|
|
RouteDecision::Local => Ok(EndpointOutcome::Local(LocalEvent::Fault {
|
2026-04-25 12:41:10 -06:00
|
|
|
header,
|
|
|
|
|
message,
|
2026-04-25 15:35:08 -06:00
|
|
|
hook_key: key,
|
2026-04-25 12:41:10 -06:00
|
|
|
})),
|
2026-04-25 20:47:37 -06:00
|
|
|
route => Ok(EndpointOutcome::Forward {
|
2026-04-25 12:41:10 -06:00
|
|
|
route,
|
2026-04-25 20:47:37 -06:00
|
|
|
frame: encode_packet(&header, &message)?,
|
|
|
|
|
}),
|
2026-04-24 14:25:35 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn handle_local_data(
|
|
|
|
|
&mut self,
|
|
|
|
|
header: PacketHeader,
|
|
|
|
|
message: DataMessage,
|
|
|
|
|
) -> Result<EndpointOutcome, EndpointError> {
|
2026-04-24 16:19:42 -06:00
|
|
|
let hook_id = header.hook_id.expect("validated");
|
2026-04-25 23:42:28 -06:00
|
|
|
let host_key = HookKey::new(self.path.clone(), hook_id);
|
|
|
|
|
let key = if let Some(key) = self
|
|
|
|
|
.hooks
|
|
|
|
|
.resolve_active_key_for_host(&host_key, &header.src_path)
|
2026-04-25 17:42:39 -06:00
|
|
|
{
|
|
|
|
|
key
|
2026-04-25 23:42:28 -06:00
|
|
|
} else if self.hooks.pending(&host_key).is_some_and(|pending| {
|
|
|
|
|
pending.caller_src_path == header.src_path
|
|
|
|
|
&& pending.procedure_id == message.procedure_id
|
|
|
|
|
}) {
|
|
|
|
|
self.hooks.activate_pending(&host_key);
|
|
|
|
|
host_key
|
2026-04-25 17:42:39 -06:00
|
|
|
} else {
|
2026-04-25 23:42:28 -06:00
|
|
|
return Ok(EndpointOutcome::Dropped);
|
2026-04-25 11:57:37 -06:00
|
|
|
};
|
2026-04-24 14:25:35 -06:00
|
|
|
|
|
|
|
|
let Some(active) = self.hooks.active(&key) else {
|
2026-04-25 20:47:37 -06:00
|
|
|
return Ok(EndpointOutcome::Dropped);
|
2026-04-24 14:25:35 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if active.peer_path != header.src_path {
|
2026-04-25 13:34:18 -06:00
|
|
|
// A reused hook id from the wrong peer is treated as terminal for this hook,
|
|
|
|
|
// because the endpoint can no longer trust future traffic on it.
|
2026-04-24 14:25:35 -06:00
|
|
|
self.hooks.remove_active(&key);
|
2026-04-25 17:42:39 -06:00
|
|
|
return self.emit_fault_if_possible(Some(key), ProtocolFault::INVALID_HOOK_PEER);
|
2026-04-24 14:25:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if active.procedure_id != message.procedure_id {
|
2026-04-25 13:34:18 -06:00
|
|
|
// Data frames stay bound to the procedure chosen by the original call.
|
2026-04-25 20:47:37 -06:00
|
|
|
return Ok(EndpointOutcome::Dropped);
|
2026-04-24 14:25:35 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-25 12:15:38 -06:00
|
|
|
if message.end_hook && self.hooks.mark_peer_end(&key) {
|
2026-04-24 14:25:35 -06:00
|
|
|
self.hooks.remove_active(&key);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-25 20:47:37 -06:00
|
|
|
Ok(EndpointOutcome::Local(LocalEvent::Data {
|
2026-04-25 15:35:08 -06:00
|
|
|
header,
|
|
|
|
|
message,
|
|
|
|
|
hook_key: key,
|
|
|
|
|
}))
|
2026-04-24 14:25:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn handle_local_fault(
|
|
|
|
|
&mut self,
|
|
|
|
|
header: PacketHeader,
|
|
|
|
|
message: FaultMessage,
|
|
|
|
|
) -> Result<EndpointOutcome, EndpointError> {
|
2026-04-25 12:37:54 -06:00
|
|
|
let hook_id = header.hook_id.expect("validated");
|
2026-04-25 23:42:28 -06:00
|
|
|
let pending_key = HookKey::new(self.path.clone(), hook_id);
|
2026-04-25 12:41:10 -06:00
|
|
|
if let Some(key) = self
|
|
|
|
|
.hooks
|
2026-04-25 23:42:28 -06:00
|
|
|
.resolve_active_key_for_host(&pending_key, &header.src_path)
|
2026-04-25 12:41:10 -06:00
|
|
|
{
|
2026-04-25 12:37:54 -06:00
|
|
|
self.hooks.remove_active(&key);
|
2026-04-25 20:47:37 -06:00
|
|
|
return Ok(EndpointOutcome::Local(LocalEvent::Fault {
|
2026-04-25 12:41:10 -06:00
|
|
|
header,
|
|
|
|
|
message,
|
2026-04-25 15:35:08 -06:00
|
|
|
hook_key: key,
|
2026-04-25 12:41:10 -06:00
|
|
|
}));
|
2026-04-25 12:37:54 -06:00
|
|
|
}
|
2026-04-24 14:25:35 -06:00
|
|
|
|
2026-04-25 12:37:54 -06:00
|
|
|
if self
|
|
|
|
|
.hooks
|
|
|
|
|
.pending(&pending_key)
|
|
|
|
|
.is_some_and(|pending| pending.caller_src_path == header.src_path)
|
|
|
|
|
{
|
|
|
|
|
self.hooks.remove_pending(&pending_key);
|
2026-04-25 20:47:37 -06:00
|
|
|
return Ok(EndpointOutcome::Local(LocalEvent::Fault {
|
2026-04-25 12:41:10 -06:00
|
|
|
header,
|
|
|
|
|
message,
|
2026-04-25 15:35:08 -06:00
|
|
|
hook_key: pending_key,
|
2026-04-25 12:41:10 -06:00
|
|
|
}));
|
2026-04-25 12:37:54 -06:00
|
|
|
}
|
2026-04-24 14:25:35 -06:00
|
|
|
|
2026-04-25 20:47:37 -06:00
|
|
|
Ok(EndpointOutcome::Dropped)
|
2026-04-24 14:25:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn decide_route(&self, dst_path: &[String]) -> RouteDecision {
|
2026-04-25 12:15:38 -06:00
|
|
|
self.routing.route(dst_path)
|
2026-04-24 14:25:35 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-24 14:27:55 -06:00
|
|
|
pub(crate) fn valid_source_for_ingress(&self, ingress: &Ingress, src_path: &[String]) -> bool {
|
2026-04-24 14:25:35 -06:00
|
|
|
match ingress {
|
|
|
|
|
Ingress::Parent => {
|
2026-04-25 13:34:18 -06:00
|
|
|
// Parent ingress may carry packets from ancestors, siblings, or the endpoint
|
|
|
|
|
// itself, but not from descendants pretending to be upstream.
|
2026-04-24 14:25:35 -06:00
|
|
|
if src_path.len() < self.path.len() {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if src_path.len() == self.path.len() {
|
|
|
|
|
return src_path == self.path;
|
|
|
|
|
}
|
|
|
|
|
!src_path.starts_with(&self.path)
|
|
|
|
|
}
|
|
|
|
|
Ingress::Child(child_path) => src_path.starts_with(child_path),
|
|
|
|
|
Ingress::Local => src_path == self.path,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|