openrtc/
scoped_streams.rs1use crate::{
4 application_crypto_streams::{ApplicationStreamAdmission, PeerRecvStream, PeerSendStream},
5 native_node::{IncomingStream, IncomingStreamType},
6 stream_metadata::{decode_prefix, ChannelMetadata, DecodeDecision},
7 Client,
8};
9use anyhow::{bail, ensure, Context, Result};
10
11const MAX_ENVELOPE: usize = 8192;
12
13pub struct ScopedIncomingStream {
16 pub channel: ChannelMetadata,
17 pub endpoint_id: iroh::EndpointId,
18 pub transport_stable_id: u64,
19 pub send: PeerSendStream,
20 pub recv: PeerRecvStream,
21}
22
23impl Client {
24 pub async fn accept_scoped_protected_bi(
29 &self,
30 incoming: IncomingStream,
31 timeout_ms: Option<u64>,
32 ) -> Result<ScopedIncomingStream> {
33 let timeout =
34 std::time::Duration::from_millis(timeout_ms.unwrap_or(10_000).clamp(1, 10_000));
35 tokio::time::timeout(timeout, async {
36 let IncomingStreamType::Bi(send, recv) = incoming.stream else {
37 bail!("scoped product channel requires a bidirectional stream");
38 };
39 let (send, mut recv) = self
40 .wrap_bi_with_prefix_for_admission(
41 &incoming.endpoint_id,
42 incoming.transport_stable_id,
43 send,
44 recv,
45 &incoming.recv_prefix,
46 ApplicationStreamAdmission::Envelope,
47 )
48 .await?;
49 let mut bytes = Vec::new();
50 let channel = loop {
51 match decode_prefix(&bytes) {
52 DecodeDecision::Decoded(prefix) => {
53 ensure!(
54 prefix.consumed_len == bytes.len(),
55 "invalid scoped channel envelope boundary"
56 );
57 break prefix.channel;
58 }
59 DecodeDecision::NotMatched => bail!("missing scoped channel envelope"),
60 DecodeDecision::NeedMore(n) => {
61 ensure!(
62 n > bytes.len() && n <= MAX_ENVELOPE,
63 "invalid scoped channel envelope length"
64 );
65 let start = bytes.len();
66 bytes.resize(n, 0);
67 let mut offset = start;
68 while offset < n {
69 let read = recv.read(&mut bytes[offset..]).await?;
70 ensure!(read > 0, "scoped channel stream ended");
71 offset += read;
72 }
73 }
74 }
75 };
76 let scope = channel
77 .metadata
78 .as_ref()
79 .and_then(|m| m.get("scope"))
80 .and_then(|s| s.as_str())
81 .context("missing named application stream scope")?;
82 ensure!(
83 !scope.is_empty() && scope.len() <= 1024,
84 "invalid application stream scope"
85 );
86 let local = self
87 .current_node_id()
88 .await
89 .context("local application identity is unavailable")?;
90 let id = Self::deterministic_connection_id(&local, &incoming.endpoint_id.to_string());
91 ensure!(
92 self.connection_application_crypto_is_confirmed(
93 &id,
94 Some(incoming.transport_stable_id)
95 ),
96 "scoped stream crypto generation is not confirmed"
97 );
98 let (send, recv) = self.bind_scoped_application_streams(&id, scope, send, recv)?;
99 let current = self
100 .get_connection(incoming.endpoint_id)
101 .await
102 .map(|c| crate::transport_generation::for_connection(&c));
103 ensure!(
104 current == Some(incoming.transport_stable_id),
105 "scoped incoming transport generation is retired"
106 );
107 send.binding()
110 .context("scoped stream protection missing")?
111 .0
112 .check()?;
113 Ok(ScopedIncomingStream {
114 channel,
115 endpoint_id: incoming.endpoint_id,
116 transport_stable_id: incoming.transport_stable_id,
117 send,
118 recv,
119 })
120 })
121 .await
122 .context("scoped channel envelope timed out")?
123 }
124
125 fn bind_scoped_application_streams(
128 &self,
129 id: &str,
130 scope: &str,
131 send: PeerSendStream,
132 recv: PeerRecvStream,
133 ) -> Result<(PeerSendStream, PeerRecvStream)> {
134 let (bootstrap, key) = send
135 .binding()
136 .context("scoped stream must be application-protected")?;
137 ensure!(
138 recv.binding()
139 .is_some_and(|access| bootstrap.same_fence(access)),
140 "mismatched scoped stream halves"
141 );
142 bootstrap.check()?;
143 let access = self.application_stream_access_for_admission(
144 id,
145 key,
146 ApplicationStreamAdmission::Scope(scope.into()),
147 )?;
148 bootstrap.check()?;
149 access.check()?;
150 Ok((send.with_access(access.clone()), recv.with_access(access)))
151 }
152}