use crate::config::LimitsConfig;
use liminal::protocol::{Frame, FrameHeader, ProtocolError, codec};
use std::io::{Read, Write};
use std::net::TcpStream;
pub mod choice;
mod reader;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Carrier {
Wire,
Direct,
Relay,
}
#[derive(Debug, PartialEq, Eq)]
pub enum CarrierReceive {
Sealed { subject: String, sealed: Vec<u8> },
Acknowledged {
message_id: u64,
durable_position: Option<u64>,
},
Refused {
reason_code: u16,
reason: Option<String>,
},
End { reason: String },
}
#[derive(Debug, thiserror::Error)]
pub enum CarrierError {
#[error("wire link lifecycle failed: {0}")]
Lifecycle(String),
#[error("wire I/O failed: {0}")]
Io(#[from] std::io::Error),
#[error("wire framing refused: {0}")]
Protocol(#[from] ProtocolError),
#[error(transparent)]
Subject(#[from] crate::auth_lys::SubjectBindingError),
#[error("wire frame of {bytes} bytes exceeds bound {bound}")]
TooLarge { bytes: usize, bound: usize },
#[error("unexpected frame on wire link")]
UnexpectedFrame,
#[error("wire carrier has ended")]
Ended,
}
pub trait LinkCarrier: Send + std::fmt::Debug {
fn carrier(&self) -> Carrier;
fn refuse_unsolicited(&self) -> Result<(), CarrierError> {
Ok(())
}
fn send(&mut self, subject: &str, sealed: &[u8]) -> Result<(), CarrierError>;
fn receive(&mut self) -> Result<CarrierReceive, CarrierError>;
}
#[derive(Debug)]
pub struct WireCarrier {
socket: TcpStream,
stream_id: u32,
ended: Option<String>,
life: Option<crate::link::LinkLife>,
reader: Option<reader::Reader>,
}
impl WireCarrier {
pub(crate) fn refuse_unsolicited(&self) -> Result<(), CarrierError> {
if let Some(reader) = &self.reader
&& let Some(answer) = reader.try_receive()?
{
let error = match answer {
Ok(Some(_)) => CarrierError::UnexpectedFrame,
Ok(None) => CarrierError::Ended,
Err(error) => error,
};
self.failed(&error);
return Err(error);
}
Ok(())
}
pub(crate) fn install_life(&mut self, life: crate::link::LinkLife) -> Result<(), CarrierError> {
if self.life.is_some() {
return Err(CarrierError::Lifecycle(
"lifecycle already installed".to_owned(),
));
}
self.life = Some(life.clone());
match reader::Reader::start(&self.socket, self.stream_id, life) {
Ok(reader) => self.reader = Some(reader),
Err(error) => {
self.end_life(crate::link::LinkEnd::Refused(error.to_string()));
return Err(error);
}
}
Ok(())
}
pub(crate) const fn life(&self) -> Option<&crate::link::LinkLife> {
self.life.as_ref()
}
fn end_life(&self, reason: crate::link::LinkEnd) {
if let Some(life) = &self.life
&& let Err(error) = life.end(reason)
{
tracing::error!(%error, "wire link end observation failed");
}
}
fn failed(&self, error: &CarrierError) {
self.end_life(
if matches!(error, CarrierError::Io(_) | CarrierError::Ended) {
crate::link::LinkEnd::PeerGone
} else {
crate::link::LinkEnd::Refused(error.to_string())
},
);
}
pub fn interrupt_handle(&self) -> Result<TcpStream, CarrierError> {
Ok(self.socket.try_clone()?)
}
pub fn new(socket: TcpStream, stream_id: u32) -> Result<Self, CarrierError> {
liminal::protocol::validate_stream(liminal::protocol::FrameType::LinkMessage, stream_id)?;
Ok(Self {
socket,
stream_id,
ended: None,
life: None,
reader: None,
})
}
}
impl LinkCarrier for WireCarrier {
fn refuse_unsolicited(&self) -> Result<(), CarrierError> {
Self::refuse_unsolicited(self)
}
fn carrier(&self) -> Carrier {
Carrier::Wire
}
fn send(&mut self, subject: &str, sealed: &[u8]) -> Result<(), CarrierError> {
if self.ended.is_some() {
return Err(CarrierError::Ended);
}
if let Some(life) = &self.life
&& life.reason().map_err(CarrierError::Lifecycle)?.is_some()
{
return Err(CarrierError::Ended);
}
crate::auth_lys::SubjectBinding::new(
"wire".to_owned(),
subject.to_owned(),
subject.to_owned(),
)?;
let result = write_frame(
&mut self.socket,
&Frame::LinkMessage {
flags: 0,
stream_id: self.stream_id,
subject: subject.to_owned(),
sealed: sealed.to_vec(),
},
);
if let Err(error) = &result {
self.failed(error);
}
result
}
fn receive(&mut self) -> Result<CarrierReceive, CarrierError> {
if let Some(reason) = &self.ended {
return Ok(CarrierReceive::End {
reason: reason.clone(),
});
}
let answer = if let Some(reader) = &self.reader {
reader.receive()
} else {
read_frame(&mut self.socket)
};
let frame = answer.inspect_err(|error| self.failed(error))?;
let reason = match frame {
Some(Frame::PublishAck {
stream_id,
message_id,
durable_position,
..
}) if stream_id == self.stream_id => {
return Ok(CarrierReceive::Acknowledged {
message_id,
durable_position,
});
}
Some(Frame::PublishError {
stream_id,
reason_code,
message,
..
}) if stream_id == self.stream_id => {
return Ok(CarrierReceive::Refused {
reason_code,
reason: message,
});
}
Some(Frame::LinkMessage {
stream_id,
subject,
sealed,
..
}) if stream_id == self.stream_id => {
crate::auth_lys::SubjectBinding::new(
"wire".to_owned(),
subject.clone(),
subject.clone(),
)?;
return Ok(CarrierReceive::Sealed { subject, sealed });
}
Some(Frame::DisconnectReason { reason, .. }) => reason.name().to_owned(),
Some(Frame::Disconnect { .. }) => "peer_disconnect".to_owned(),
None => "wire_eof".to_owned(),
Some(_) => {
self.failed(&CarrierError::UnexpectedFrame);
return Err(CarrierError::UnexpectedFrame);
}
};
self.end_life(
if reason == "grant_revoked" || reason == "revocation_log_reset" {
crate::link::LinkEnd::Revoked
} else {
crate::link::LinkEnd::PeerGone
},
);
self.ended = Some(reason.clone());
Ok(CarrierReceive::End { reason })
}
}
impl Drop for WireCarrier {
fn drop(&mut self) {
self.end_life(crate::link::LinkEnd::Shutdown);
if let Some(reader) = self.reader.take() {
reader.close(&self.socket);
}
}
}
#[derive(Debug)]
pub struct RelayCarrier {
wire: WireCarrier,
relay: crate::link_handshake::MachineIdentity,
}
impl RelayCarrier {
pub fn new(
socket: TcpStream,
stream_id: u32,
relay: crate::link_handshake::MachineIdentity,
) -> Result<Self, CarrierError> {
Ok(Self {
wire: WireCarrier::new(socket, stream_id)?,
relay,
})
}
pub fn interrupt_handle(&self) -> Result<TcpStream, CarrierError> {
self.wire.interrupt_handle()
}
pub fn acknowledge(
&mut self,
message_id: u64,
durable_position: Option<u64>,
) -> Result<(), CarrierError> {
self.reply(&Frame::PublishAck {
flags: 0,
stream_id: self.wire.stream_id,
message_id,
durable_position,
})
}
pub fn refuse(&mut self, reason_code: u16, reason: &str) -> Result<(), CarrierError> {
self.reply(&Frame::PublishError {
flags: 0,
stream_id: self.wire.stream_id,
reason_code,
message: Some(reason.to_owned()),
})
}
#[must_use]
pub fn topology(
&self,
local: crate::link_handshake::MachineIdentity,
peer: crate::link_handshake::MachineIdentity,
counters: crate::console::records::LinkCounters,
) -> crate::console::records::Link {
crate::console::records::Link::federated(
&hex::encode(local.ed25519_public()),
&hex::encode(peer.ed25519_public()),
Some(crate::console::records::Carrier::Relay(hex::encode(
self.relay.ed25519_public(),
))),
crate::console::records::LinkState::Up,
None,
counters,
)
}
fn reply(&mut self, frame: &Frame) -> Result<(), CarrierError> {
if self.wire.ended.is_some() {
return Err(CarrierError::Ended);
}
write_frame(&mut self.wire.socket, frame)
}
}
impl LinkCarrier for RelayCarrier {
fn refuse_unsolicited(&self) -> Result<(), CarrierError> {
self.wire.refuse_unsolicited()
}
fn carrier(&self) -> Carrier {
Carrier::Relay
}
fn send(&mut self, subject: &str, sealed: &[u8]) -> Result<(), CarrierError> {
self.wire.send(subject, sealed)
}
fn receive(&mut self) -> Result<CarrierReceive, CarrierError> {
self.wire.receive()
}
}
pub(crate) const WIRE_FRAME_BOUND: usize = LimitsConfig::DEFAULT_MAX_CONNECTION_INBOX_BYTES;
pub(crate) fn write_frame(socket: &mut TcpStream, frame: &Frame) -> Result<(), CarrierError> {
let bytes = codec::encoded_len(frame)?;
if bytes > WIRE_FRAME_BOUND {
return Err(CarrierError::TooLarge {
bytes,
bound: WIRE_FRAME_BOUND,
});
}
let mut encoded = vec![0; bytes];
let length = codec::encode(frame, &mut encoded)?;
socket.write_all(&encoded[..length])?;
Ok(())
}
pub(crate) fn read_frame(socket: &mut TcpStream) -> Result<Option<Frame>, CarrierError> {
let mut header = [0; FrameHeader::WIRE_LEN];
loop {
match socket.read(&mut header[..1]) {
Ok(0) => return Ok(None),
Ok(_) => break,
Err(error) if error.kind() == std::io::ErrorKind::Interrupted => {}
Err(error) => return Err(error.into()),
}
}
socket.read_exact(&mut header[1..])?;
let payload = u32::from_be_bytes([header[6], header[7], header[8], header[9]]);
let bytes = usize::try_from(payload)
.ok()
.and_then(|length| length.checked_add(header.len()))
.ok_or(CarrierError::TooLarge {
bytes: usize::MAX,
bound: WIRE_FRAME_BOUND,
})?;
if bytes > WIRE_FRAME_BOUND {
return Err(CarrierError::TooLarge {
bytes,
bound: WIRE_FRAME_BOUND,
});
}
let mut encoded = vec![0; bytes];
encoded[..header.len()].copy_from_slice(&header);
socket.read_exact(&mut encoded[header.len()..])?;
let (frame, _) = codec::decode(&encoded)?;
Ok(Some(frame))
}