Skip to main content

liminal_server/
link_carrier.rs

1//! Carriers move sealed link bytes without opening the Noise transport.
2
3use crate::config::LimitsConfig;
4use liminal::protocol::{Frame, FrameHeader, ProtocolError, codec};
5use std::io::{Read, Write};
6use std::net::TcpStream;
7
8pub mod choice;
9mod reader;
10
11/// The route a keyed link's bytes take.
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub enum Carrier {
14    /// The peer's TCP wire listener.
15    Wire,
16    /// A direct datagram path.
17    Direct,
18    /// A relay carrying opaque bytes.
19    Relay,
20}
21
22/// The next sealed message, or the carrier's named end.
23#[derive(Debug, PartialEq, Eq)]
24pub enum CarrierReceive {
25    /// Bytes still sealed by the keyed link.
26    Sealed { subject: String, sealed: Vec<u8> },
27    /// The importing end acknowledged the message on this link's stream.
28    Acknowledged {
29        message_id: u64,
30        durable_position: Option<u64>,
31    },
32    /// The importing end refused the message on this link's stream.
33    Refused {
34        reason_code: u16,
35        reason: Option<String>,
36    },
37    /// The peer or byte stream ended the carrier.
38    End { reason: String },
39}
40
41/// Why a carrier could not move a frame.
42#[derive(Debug, thiserror::Error)]
43pub enum CarrierError {
44    /// The carrier's owned lifecycle or reader could not continue.
45    #[error("wire link lifecycle failed: {0}")]
46    Lifecycle(String),
47    /// A socket operation failed; credential and message bytes are excluded.
48    #[error("wire I/O failed: {0}")]
49    Io(#[from] std::io::Error),
50    /// The canonical codec refused a frame.
51    #[error("wire framing refused: {0}")]
52    Protocol(#[from] ProtocolError),
53    /// The subject is not a placed name.
54    #[error(transparent)]
55    Subject(#[from] crate::auth_lys::SubjectBindingError),
56    /// A peer's declared frame exceeded the allocation bound.
57    #[error("wire frame of {bytes} bytes exceeds bound {bound}")]
58    TooLarge { bytes: usize, bound: usize },
59    /// The peer answered with a different frame or stream.
60    #[error("unexpected frame on wire link")]
61    UnexpectedFrame,
62    /// Sending after an observed carrier end was refused.
63    #[error("wire carrier has ended")]
64    Ended,
65}
66
67/// A carrier transports sealed bytes and reports its end by name.
68pub trait LinkCarrier: Send + std::fmt::Debug {
69    /// Which carrier holds this path.
70    fn carrier(&self) -> Carrier;
71    /// # Errors
72    /// A parked reader observed an unsolicited frame or a carrier end.
73    fn refuse_unsolicited(&self) -> Result<(), CarrierError> {
74        Ok(())
75    }
76    /// Sends one subject and its sealed bytes.
77    ///
78    /// # Errors
79    /// The frame was invalid or the carrier could not send it.
80    fn send(&mut self, subject: &str, sealed: &[u8]) -> Result<(), CarrierError>;
81    /// Receives the next sealed bytes, or the carrier's named end.
82    ///
83    /// # Errors
84    /// An I/O error, malformed frame, or unexpected frame/stream.
85    fn receive(&mut self) -> Result<CarrierReceive, CarrierError>;
86}
87
88/// One application stream on the peer's TCP wire connection.
89#[derive(Debug)]
90pub struct WireCarrier {
91    socket: TcpStream,
92    stream_id: u32,
93    ended: Option<String>,
94    life: Option<crate::link::LinkLife>,
95    reader: Option<reader::Reader>,
96}
97
98impl WireCarrier {
99    pub(crate) fn refuse_unsolicited(&self) -> Result<(), CarrierError> {
100        if let Some(reader) = &self.reader
101            && let Some(answer) = reader.try_receive()?
102        {
103            let error = match answer {
104                Ok(Some(_)) => CarrierError::UnexpectedFrame,
105                Ok(None) => CarrierError::Ended,
106                Err(error) => error,
107            };
108            self.failed(&error);
109            return Err(error);
110        }
111        Ok(())
112    }
113    pub(crate) fn install_life(&mut self, life: crate::link::LinkLife) -> Result<(), CarrierError> {
114        if self.life.is_some() {
115            return Err(CarrierError::Lifecycle(
116                "lifecycle already installed".to_owned(),
117            ));
118        }
119        self.life = Some(life.clone());
120        match reader::Reader::start(&self.socket, self.stream_id, life) {
121            Ok(reader) => self.reader = Some(reader),
122            Err(error) => {
123                self.end_life(crate::link::LinkEnd::Refused(error.to_string()));
124                return Err(error);
125            }
126        }
127        Ok(())
128    }
129
130    pub(crate) const fn life(&self) -> Option<&crate::link::LinkLife> {
131        self.life.as_ref()
132    }
133
134    fn end_life(&self, reason: crate::link::LinkEnd) {
135        if let Some(life) = &self.life
136            && let Err(error) = life.end(reason)
137        {
138            tracing::error!(%error, "wire link end observation failed");
139        }
140    }
141
142    fn failed(&self, error: &CarrierError) {
143        self.end_life(
144            if matches!(error, CarrierError::Io(_) | CarrierError::Ended) {
145                crate::link::LinkEnd::PeerGone
146            } else {
147                crate::link::LinkEnd::Refused(error.to_string())
148            },
149        );
150    }
151    /// Duplicates the carrier socket solely to interrupt its blocking receive at stop.
152    ///
153    /// # Errors
154    /// The socket cannot be duplicated.
155    pub fn interrupt_handle(&self) -> Result<TcpStream, CarrierError> {
156        Ok(self.socket.try_clone()?)
157    }
158    /// Takes ownership of a connection whose link handshake has finished.
159    ///
160    /// # Errors
161    /// Stream zero is reserved for connection control.
162    pub fn new(socket: TcpStream, stream_id: u32) -> Result<Self, CarrierError> {
163        liminal::protocol::validate_stream(liminal::protocol::FrameType::LinkMessage, stream_id)?;
164        Ok(Self {
165            socket,
166            stream_id,
167            ended: None,
168            life: None,
169            reader: None,
170        })
171    }
172}
173
174impl LinkCarrier for WireCarrier {
175    fn refuse_unsolicited(&self) -> Result<(), CarrierError> {
176        Self::refuse_unsolicited(self)
177    }
178    fn carrier(&self) -> Carrier {
179        Carrier::Wire
180    }
181
182    fn send(&mut self, subject: &str, sealed: &[u8]) -> Result<(), CarrierError> {
183        if self.ended.is_some() {
184            return Err(CarrierError::Ended);
185        }
186        if let Some(life) = &self.life
187            && life.reason().map_err(CarrierError::Lifecycle)?.is_some()
188        {
189            return Err(CarrierError::Ended);
190        }
191        crate::auth_lys::SubjectBinding::new(
192            "wire".to_owned(),
193            subject.to_owned(),
194            subject.to_owned(),
195        )?;
196        let result = write_frame(
197            &mut self.socket,
198            &Frame::LinkMessage {
199                flags: 0,
200                stream_id: self.stream_id,
201                subject: subject.to_owned(),
202                sealed: sealed.to_vec(),
203            },
204        );
205        if let Err(error) = &result {
206            self.failed(error);
207        }
208        result
209    }
210
211    fn receive(&mut self) -> Result<CarrierReceive, CarrierError> {
212        if let Some(reason) = &self.ended {
213            return Ok(CarrierReceive::End {
214                reason: reason.clone(),
215            });
216        }
217        let answer = if let Some(reader) = &self.reader {
218            reader.receive()
219        } else {
220            read_frame(&mut self.socket)
221        };
222        let frame = answer.inspect_err(|error| self.failed(error))?;
223        let reason = match frame {
224            Some(Frame::PublishAck {
225                stream_id,
226                message_id,
227                durable_position,
228                ..
229            }) if stream_id == self.stream_id => {
230                return Ok(CarrierReceive::Acknowledged {
231                    message_id,
232                    durable_position,
233                });
234            }
235            Some(Frame::PublishError {
236                stream_id,
237                reason_code,
238                message,
239                ..
240            }) if stream_id == self.stream_id => {
241                return Ok(CarrierReceive::Refused {
242                    reason_code,
243                    reason: message,
244                });
245            }
246            Some(Frame::LinkMessage {
247                stream_id,
248                subject,
249                sealed,
250                ..
251            }) if stream_id == self.stream_id => {
252                crate::auth_lys::SubjectBinding::new(
253                    "wire".to_owned(),
254                    subject.clone(),
255                    subject.clone(),
256                )?;
257                return Ok(CarrierReceive::Sealed { subject, sealed });
258            }
259            Some(Frame::DisconnectReason { reason, .. }) => reason.name().to_owned(),
260            Some(Frame::Disconnect { .. }) => "peer_disconnect".to_owned(),
261            None => "wire_eof".to_owned(),
262            Some(_) => {
263                self.failed(&CarrierError::UnexpectedFrame);
264                return Err(CarrierError::UnexpectedFrame);
265            }
266        };
267        self.end_life(
268            if reason == "grant_revoked" || reason == "revocation_log_reset" {
269                crate::link::LinkEnd::Revoked
270            } else {
271                crate::link::LinkEnd::PeerGone
272            },
273        );
274        self.ended = Some(reason.clone());
275        Ok(CarrierReceive::End { reason })
276    }
277}
278
279impl Drop for WireCarrier {
280    fn drop(&mut self) {
281        self.end_life(crate::link::LinkEnd::Shutdown);
282        if let Some(reader) = self.reader.take() {
283            reader.close(&self.socket);
284        }
285    }
286}
287
288/// An ordinary wire connection to the relay on its admitted relay stream.
289#[derive(Debug)]
290pub struct RelayCarrier {
291    wire: WireCarrier,
292    relay: crate::link_handshake::MachineIdentity,
293}
294
295impl RelayCarrier {
296    /// Takes a connection whose relay stream has been granted and marked.
297    ///
298    /// # Errors
299    /// Stream zero is reserved for connection control.
300    pub fn new(
301        socket: TcpStream,
302        stream_id: u32,
303        relay: crate::link_handshake::MachineIdentity,
304    ) -> Result<Self, CarrierError> {
305        Ok(Self {
306            wire: WireCarrier::new(socket, stream_id)?,
307            relay,
308        })
309    }
310
311    /// Interrupts a blocking receive through the same owned socket.
312    ///
313    /// # Errors
314    /// The socket cannot be duplicated.
315    pub fn interrupt_handle(&self) -> Result<TcpStream, CarrierError> {
316        self.wire.interrupt_handle()
317    }
318
319    /// Answers an imported frame; the relay carries the full receipt back.
320    ///
321    /// # Errors
322    /// The carrier has ended or the ordinary connection cannot send the ack.
323    pub fn acknowledge(
324        &mut self,
325        message_id: u64,
326        durable_position: Option<u64>,
327    ) -> Result<(), CarrierError> {
328        self.reply(&Frame::PublishAck {
329            flags: 0,
330            stream_id: self.wire.stream_id,
331            message_id,
332            durable_position,
333        })
334    }
335
336    /// Answers a named endpoint refusal on the relay stream.
337    ///
338    /// # Errors
339    /// The carrier has ended or the ordinary connection cannot send the refusal.
340    pub fn refuse(&mut self, reason_code: u16, reason: &str) -> Result<(), CarrierError> {
341        self.reply(&Frame::PublishError {
342            flags: 0,
343            stream_id: self.wire.stream_id,
344            reason_code,
345            message: Some(reason.to_owned()),
346        })
347    }
348
349    /// The topology record for the endpoint's observed relayed link.
350    #[must_use]
351    pub fn topology(
352        &self,
353        local: crate::link_handshake::MachineIdentity,
354        peer: crate::link_handshake::MachineIdentity,
355        counters: crate::console::records::LinkCounters,
356    ) -> crate::console::records::Link {
357        crate::console::records::Link::federated(
358            &hex::encode(local.ed25519_public()),
359            &hex::encode(peer.ed25519_public()),
360            Some(crate::console::records::Carrier::Relay(hex::encode(
361                self.relay.ed25519_public(),
362            ))),
363            crate::console::records::LinkState::Up,
364            None,
365            counters,
366        )
367    }
368
369    fn reply(&mut self, frame: &Frame) -> Result<(), CarrierError> {
370        if self.wire.ended.is_some() {
371            return Err(CarrierError::Ended);
372        }
373        write_frame(&mut self.wire.socket, frame)
374    }
375}
376
377impl LinkCarrier for RelayCarrier {
378    fn refuse_unsolicited(&self) -> Result<(), CarrierError> {
379        self.wire.refuse_unsolicited()
380    }
381    fn carrier(&self) -> Carrier {
382        Carrier::Relay
383    }
384    fn send(&mut self, subject: &str, sealed: &[u8]) -> Result<(), CarrierError> {
385        self.wire.send(subject, sealed)
386    }
387    fn receive(&mut self) -> Result<CarrierReceive, CarrierError> {
388        self.wire.receive()
389    }
390}
391
392/// The same bound is enforced before encoding and before allocating a peer's body.
393pub(crate) const WIRE_FRAME_BOUND: usize = LimitsConfig::DEFAULT_MAX_CONNECTION_INBOX_BYTES;
394
395pub(crate) fn write_frame(socket: &mut TcpStream, frame: &Frame) -> Result<(), CarrierError> {
396    let bytes = codec::encoded_len(frame)?;
397    if bytes > WIRE_FRAME_BOUND {
398        return Err(CarrierError::TooLarge {
399            bytes,
400            bound: WIRE_FRAME_BOUND,
401        });
402    }
403    let mut encoded = vec![0; bytes];
404    let length = codec::encode(frame, &mut encoded)?;
405    socket.write_all(&encoded[..length])?;
406    Ok(())
407}
408
409pub(crate) fn read_frame(socket: &mut TcpStream) -> Result<Option<Frame>, CarrierError> {
410    let mut header = [0; FrameHeader::WIRE_LEN];
411    // Only EOF before a header is a clean end; an incomplete header/body is an error.
412    loop {
413        match socket.read(&mut header[..1]) {
414            Ok(0) => return Ok(None),
415            Ok(_) => break,
416            Err(error) if error.kind() == std::io::ErrorKind::Interrupted => {}
417            Err(error) => return Err(error.into()),
418        }
419    }
420    socket.read_exact(&mut header[1..])?;
421    let payload = u32::from_be_bytes([header[6], header[7], header[8], header[9]]);
422    let bytes = usize::try_from(payload)
423        .ok()
424        .and_then(|length| length.checked_add(header.len()))
425        .ok_or(CarrierError::TooLarge {
426            bytes: usize::MAX,
427            bound: WIRE_FRAME_BOUND,
428        })?;
429    if bytes > WIRE_FRAME_BOUND {
430        return Err(CarrierError::TooLarge {
431            bytes,
432            bound: WIRE_FRAME_BOUND,
433        });
434    }
435    let mut encoded = vec![0; bytes];
436    encoded[..header.len()].copy_from_slice(&header);
437    socket.read_exact(&mut encoded[header.len()..])?;
438    let (frame, _) = codec::decode(&encoded)?;
439    Ok(Some(frame))
440}