1use crate::utils::codec::{append_frame, framed_len, recv_frame, send_frame};
59use commonware_codec::{DecodeExt, Encode as _, Error as CodecError, FixedSize};
60use commonware_cryptography::{
61 handshake::{
62 self, dial_end, dial_start, listen_end, listen_start, Ack, Context,
63 Error as HandshakeError, RecvCipher, SendCipher, Syn, SynAck,
64 },
65 transcript::Transcript,
66 Signer,
67};
68use commonware_formatting::hex;
69use commonware_macros::select;
70use commonware_runtime::{
71 BufMut, BufferPool, BufferPooler, Clock, Error as RuntimeError, IoBuf, IoBufMut, IoBufs, Sink,
72 Stream,
73};
74use commonware_utils::SystemTimeExt;
75use rand_core::CryptoRng;
76use std::{future::Future, ops::Range, time::Duration};
77use thiserror::Error;
78
79const TAG_SIZE: u32 = {
80 assert!(handshake::TAG_SIZE <= u32::MAX as usize);
81 handshake::TAG_SIZE as u32
82};
83
84#[derive(Error, Debug)]
86pub enum Error {
87 #[error("handshake error: {0}")]
88 HandshakeError(HandshakeError),
89 #[error("unable to decode: {0}")]
90 UnableToDecode(CodecError),
91 #[error("peer rejected: {}", hex(_0))]
92 PeerRejected(Vec<u8>),
93 #[error("recv failed")]
94 RecvFailed(RuntimeError),
95 #[error("recv too large: {0} bytes")]
96 RecvTooLarge(usize),
97 #[error("invalid varint length prefix")]
98 InvalidVarint,
99 #[error("send failed")]
100 SendFailed(RuntimeError),
101 #[error("send zero size")]
102 SendZeroSize,
103 #[error("send too large: {0} bytes")]
104 SendTooLarge(usize),
105 #[error("connection closed")]
106 StreamClosed,
107 #[error("handshake timed out")]
108 HandshakeTimeout,
109}
110
111impl From<CodecError> for Error {
112 fn from(value: CodecError) -> Self {
113 Self::UnableToDecode(value)
114 }
115}
116
117impl From<HandshakeError> for Error {
118 fn from(value: HandshakeError) -> Self {
119 Self::HandshakeError(value)
120 }
121}
122
123#[derive(Clone)]
130pub struct Config<S> {
131 pub signing_key: S,
135
136 pub namespace: Vec<u8>,
139
140 pub max_message_size: u32,
145
146 pub synchrony_bound: Duration,
148
149 pub max_handshake_age: Duration,
151
152 pub handshake_timeout: Duration,
154}
155
156impl<S> Config<S> {
157 pub fn time_information(&self, ctx: &impl Clock) -> (u64, Range<u64>) {
159 fn duration_to_u64(d: Duration) -> u64 {
160 u64::try_from(d.as_millis()).expect("duration ms should fit in an u64")
161 }
162 let current_time_ms = duration_to_u64(ctx.current().epoch());
163 let ok_timestamps = (current_time_ms
164 .saturating_sub(duration_to_u64(self.max_handshake_age)))
165 ..(current_time_ms.saturating_add(duration_to_u64(self.synchrony_bound)));
166 (current_time_ms, ok_timestamps)
167 }
168}
169
170async fn recv_handshake_frame<M, T>(stream: &mut T) -> Result<M, Error>
173where
174 M: DecodeExt<()> + FixedSize,
175 T: Stream,
176{
177 let frame = recv_frame(
178 stream,
179 u32::try_from(M::SIZE).expect("handshake frame should fit in u32"),
180 )
181 .await?;
182 Ok(M::decode(frame)?)
183}
184
185pub async fn dial<R: BufferPooler + CryptoRng + Clock, S: Signer, I: Stream, O: Sink>(
188 ctx: R,
189 config: Config<S>,
190 peer: S::PublicKey,
191 mut stream: I,
192 mut sink: O,
193) -> Result<(Sender<O>, Receiver<I>), Error> {
194 let pool = ctx.network_buffer_pool().clone();
195 let timeout = ctx.sleep(config.handshake_timeout);
196 let inner_routine = async move {
197 send_frame(
198 &mut sink,
199 config.signing_key.public_key().encode(),
200 config.max_message_size,
201 )
202 .await?;
203
204 let (current_time, ok_timestamps) = config.time_information(&ctx);
205 let (state, syn) = dial_start(
206 ctx,
207 Context::new(
208 &Transcript::new(&config.namespace),
209 current_time,
210 ok_timestamps,
211 config.signing_key,
212 peer,
213 ),
214 );
215 send_frame(&mut sink, syn.encode(), config.max_message_size).await?;
216
217 let syn_ack = recv_handshake_frame::<SynAck<S::Signature>, _>(&mut stream).await?;
218
219 let (ack, send, recv) = dial_end(state, syn_ack)?;
220 send_frame(&mut sink, ack.encode(), config.max_message_size).await?;
221
222 Ok((
223 Sender {
224 cipher: send,
225 sink,
226 max_message_size: config.max_message_size,
227 pool: pool.clone(),
228 },
229 Receiver {
230 cipher: recv,
231 stream,
232 max_message_size: config.max_message_size,
233 pool,
234 },
235 ))
236 };
237
238 select! {
239 x = inner_routine => x,
240 _ = timeout => Err(Error::HandshakeTimeout),
241 }
242}
243
244pub async fn listen<
247 R: BufferPooler + CryptoRng + Clock,
248 S: Signer,
249 I: Stream,
250 O: Sink,
251 Fut: Future<Output = bool>,
252 F: FnOnce(S::PublicKey) -> Fut,
253>(
254 ctx: R,
255 bouncer: F,
256 config: Config<S>,
257 mut stream: I,
258 mut sink: O,
259) -> Result<(S::PublicKey, Sender<O>, Receiver<I>), Error> {
260 let pool = ctx.network_buffer_pool().clone();
261 let timeout = ctx.sleep(config.handshake_timeout);
262 let inner_routine = async move {
263 let peer = recv_handshake_frame::<S::PublicKey, _>(&mut stream).await?;
264 if !bouncer(peer.clone()).await {
265 return Err(Error::PeerRejected(peer.encode().to_vec()));
266 }
267
268 let msg1 = recv_handshake_frame::<Syn<S::Signature>, _>(&mut stream).await?;
269
270 let (current_time, ok_timestamps) = config.time_information(&ctx);
271 let (state, syn_ack) = listen_start(
272 ctx,
273 Context::new(
274 &Transcript::new(&config.namespace),
275 current_time,
276 ok_timestamps,
277 config.signing_key,
278 peer.clone(),
279 ),
280 msg1,
281 )?;
282 send_frame(&mut sink, syn_ack.encode(), config.max_message_size).await?;
283
284 let ack = recv_handshake_frame::<Ack, _>(&mut stream).await?;
285
286 let (send, recv) = listen_end(state, ack)?;
287
288 Ok((
289 peer,
290 Sender {
291 cipher: send,
292 sink,
293 max_message_size: config.max_message_size,
294 pool: pool.clone(),
295 },
296 Receiver {
297 cipher: recv,
298 stream,
299 max_message_size: config.max_message_size,
300 pool,
301 },
302 ))
303 };
304
305 select! {
306 x = inner_routine => x,
307 _ = timeout => Err(Error::HandshakeTimeout),
308 }
309}
310
311pub struct Sender<O> {
313 cipher: SendCipher,
314 sink: O,
315 max_message_size: u32,
316 pool: BufferPool,
317}
318
319struct ChunkPlan {
321 messages: Vec<IoBufs>,
322 total_len: usize,
323}
324
325impl<O: Sink> Sender<O> {
326 fn encrypted_frame_len(&self, plaintext_len: usize) -> Result<usize, Error> {
330 framed_len(
331 plaintext_len + TAG_SIZE as usize,
332 self.max_message_size.saturating_add(TAG_SIZE),
333 )
334 }
335
336 fn append_encrypted_frame(
342 &mut self,
343 chunk: &mut IoBufMut,
344 mut bufs: IoBufs,
345 ) -> Result<(), Error> {
346 append_frame(
347 chunk,
348 bufs.len() + TAG_SIZE as usize,
349 self.max_message_size.saturating_add(TAG_SIZE),
350 |chunk, plaintext_offset| {
351 chunk.put(&mut bufs);
353
354 let tag = self
356 .cipher
357 .send_in_place(&mut chunk.as_mut()[plaintext_offset..])?;
358 chunk.put_slice(&tag);
359 Ok(())
360 },
361 )?;
362 Ok(())
363 }
364
365 fn build_chunk<I>(&mut self, messages: I, total_len: usize) -> Result<IoBuf, Error>
370 where
371 I: IntoIterator<Item = IoBufs>,
372 {
373 let mut chunk = self.pool.alloc(total_len);
374 for msg in messages {
375 self.append_encrypted_frame(&mut chunk, msg)?;
376 }
377 assert_eq!(chunk.len(), total_len);
378 Ok(chunk.freeze())
379 }
380
381 fn plan_chunks<B, I>(&self, bufs: I) -> Result<Vec<ChunkPlan>, Error>
386 where
387 B: Into<IoBufs>,
388 I: IntoIterator<Item = B>,
389 {
390 let bufs = bufs.into_iter();
391 let (lower, _) = bufs.size_hint();
392 let mut chunks = Vec::with_capacity(lower.max(1));
393 let mut batch = Vec::new();
394 let mut batch_total = 0usize;
395 let max_batch_size = self.pool.config().max_size.get();
396
397 for buf in bufs {
398 let msg = buf.into();
399 let frame_len = self.encrypted_frame_len(msg.len())?;
400
401 if frame_len > max_batch_size {
404 if !batch.is_empty() {
405 chunks.push(ChunkPlan {
406 messages: std::mem::take(&mut batch),
407 total_len: batch_total,
408 });
409 batch_total = 0;
410 }
411 chunks.push(ChunkPlan {
412 messages: vec![msg],
413 total_len: frame_len,
414 });
415 continue;
416 }
417
418 if batch_total.saturating_add(frame_len) > max_batch_size {
421 chunks.push(ChunkPlan {
422 messages: std::mem::take(&mut batch),
423 total_len: batch_total,
424 });
425 batch_total = 0;
426 }
427
428 batch_total += frame_len;
429 batch.push(msg);
430 }
431
432 if !batch.is_empty() {
433 chunks.push(ChunkPlan {
434 messages: batch,
435 total_len: batch_total,
436 });
437 }
438
439 Ok(chunks)
440 }
441
442 pub async fn send(&mut self, bufs: impl Into<IoBufs>) -> Result<(), Error> {
447 let bufs = bufs.into();
448 let frame_len = self.encrypted_frame_len(bufs.len())?;
449 let chunk = self.build_chunk(std::iter::once(bufs), frame_len)?;
450 self.sink.send(chunk).await.map_err(Error::SendFailed)
451 }
452
453 pub async fn send_many<B, I>(&mut self, bufs: I) -> Result<(), Error>
461 where
462 B: Into<IoBufs>,
463 I: IntoIterator<Item = B>,
464 {
465 let plans = self.plan_chunks(bufs)?;
466 if plans.is_empty() {
467 return Ok(());
468 }
469
470 let mut chunks = Vec::with_capacity(plans.len());
471 for plan in plans {
472 chunks.push(self.build_chunk(plan.messages, plan.total_len)?);
473 }
474
475 self.sink
476 .send(IoBufs::from(chunks))
477 .await
478 .map_err(Error::SendFailed)
479 }
480}
481
482pub struct Receiver<I> {
484 cipher: RecvCipher,
485 stream: I,
486 max_message_size: u32,
487 pool: BufferPool,
488}
489
490impl<I: Stream> Receiver<I> {
491 pub async fn recv(&mut self) -> Result<IoBufs, Error> {
497 let encrypted = recv_frame(
498 &mut self.stream,
499 self.max_message_size.saturating_add(TAG_SIZE),
500 )
501 .await?;
502
503 let mut decryption_buf = match encrypted
507 .try_into_single()
508 .and_then(|buf| buf.try_into_mut().map_err(IoBufs::from))
509 {
510 Ok(buf) => buf,
511 Err(mut encrypted) => {
512 let mut buf = self.pool.alloc(encrypted.len());
513 buf.put(&mut encrypted);
514 buf
515 }
516 };
517
518 let plaintext_len = self.cipher.recv_in_place(decryption_buf.as_mut())?;
520
521 decryption_buf.truncate(plaintext_len);
523
524 Ok(decryption_buf.freeze().into())
525 }
526}
527
528#[cfg(test)]
529mod test {
530 use super::*;
531 use commonware_codec::varint::UInt;
532 use commonware_cryptography::{ed25519::PrivateKey, Signer};
533 use commonware_runtime::{
534 deterministic, mocks, BufferPoolConfig, Error as RuntimeError, IoBuf, IoBufs, Runner as _,
535 Spawner as _, Supervisor as _,
536 };
537 use commonware_utils::{sync::Mutex, NZUsize};
538 use std::{
539 sync::{
540 atomic::{AtomicUsize, Ordering},
541 Arc,
542 },
543 time::Duration,
544 };
545
546 const NAMESPACE: &[u8] = b"fuzz_transport";
547 const MAX_MESSAGE_SIZE: u32 = 64 * 1024; fn transport_config(signing_key: PrivateKey) -> Config<PrivateKey> {
550 Config {
551 signing_key,
552 namespace: NAMESPACE.to_vec(),
553 max_message_size: MAX_MESSAGE_SIZE,
554 synchrony_bound: Duration::from_secs(1),
555 max_handshake_age: Duration::from_secs(1),
556 handshake_timeout: Duration::from_secs(1),
557 }
558 }
559
560 fn oversized_handshake_prefix(message: &impl commonware_codec::Encode) -> IoBuf {
561 let size = u32::try_from(message.encode().len()).expect("message length should fit in u32");
562 IoBuf::from(UInt(size + 1).encode())
563 }
564
565 struct CountingSink<S> {
566 inner: S,
567 sends: Arc<AtomicUsize>,
568 chunk_counts: Arc<Mutex<Vec<usize>>>,
569 }
570
571 impl<S> CountingSink<S> {
572 fn new(inner: S, sends: Arc<AtomicUsize>, chunk_counts: Arc<Mutex<Vec<usize>>>) -> Self {
573 Self {
574 inner,
575 sends,
576 chunk_counts,
577 }
578 }
579 }
580
581 impl<S: commonware_runtime::Sink> commonware_runtime::Sink for CountingSink<S> {
582 async fn send(&mut self, bufs: impl Into<IoBufs> + Send) -> Result<(), RuntimeError> {
583 let bufs = bufs.into();
584 self.sends.fetch_add(1, Ordering::Relaxed);
585 self.chunk_counts.lock().push(bufs.chunk_count());
586 self.inner.send(bufs).await
587 }
588 }
589
590 struct PoolingStream<S> {
596 inner: S,
597 pool: BufferPool,
598 last_alloc: Arc<Mutex<Range<usize>>>,
599 }
600
601 impl<S: commonware_runtime::Stream> commonware_runtime::Stream for PoolingStream<S> {
602 async fn recv(&mut self, len: usize) -> Result<IoBufs, RuntimeError> {
603 let mut bufs = self.inner.recv(len).await?;
604 let mut buf = self.pool.alloc(len);
605 buf.put(&mut bufs);
606 let buf = buf.freeze();
607 let start = buf.as_ref().as_ptr() as usize;
608 *self.last_alloc.lock() = start..start + buf.len();
609 Ok(buf.into())
610 }
611
612 fn peek(&self, max_len: usize) -> &[u8] {
613 self.inner.peek(max_len)
614 }
615 }
616
617 #[test]
618 fn test_can_setup_and_send_messages() -> Result<(), Error> {
619 let executor = deterministic::Runner::default();
620 executor.start(|context| async move {
621 let dialer_crypto = PrivateKey::from_seed(42);
622 let listener_crypto = PrivateKey::from_seed(24);
623
624 let (dialer_sink, listener_stream) = mocks::Channel::init();
625 let (listener_sink, dialer_stream) = mocks::Channel::init();
626
627 let dialer_config = transport_config(dialer_crypto.clone());
628 let listener_config = transport_config(listener_crypto.clone());
629
630 let listener_handle = context.child("listener").spawn(move |context| async move {
631 listen(
632 context,
633 |_| async { true },
634 listener_config,
635 listener_stream,
636 listener_sink,
637 )
638 .await
639 });
640
641 let (mut dialer_sender, mut dialer_receiver) = dial(
642 context,
643 dialer_config,
644 listener_crypto.public_key(),
645 dialer_stream,
646 dialer_sink,
647 )
648 .await?;
649
650 let (listener_peer, mut listener_sender, mut listener_receiver) =
651 listener_handle.await.unwrap()?;
652 assert_eq!(listener_peer, dialer_crypto.public_key());
653 let messages: Vec<&'static [u8]> = vec![b"A", b"B", b"C"];
654 for msg in &messages {
655 dialer_sender.send(&msg[..]).await?;
656 let syn_ack = listener_receiver.recv().await?;
657 assert_eq!(syn_ack.coalesce(), *msg);
658 listener_sender.send(&msg[..]).await?;
659 let ack = dialer_receiver.recv().await?;
660 assert_eq!(ack.coalesce(), *msg);
661 }
662 Ok(())
663 })
664 }
665
666 #[test]
667 fn test_recv_decrypts_unique_frame_in_place() -> Result<(), Error> {
668 let executor = deterministic::Runner::default();
669 executor.start(|context| async move {
670 let dialer_crypto = PrivateKey::from_seed(42);
671 let listener_crypto = PrivateKey::from_seed(24);
672
673 let (dialer_sink, listener_stream) = mocks::Channel::init();
674 let (listener_sink, dialer_stream) = mocks::Channel::init();
675
676 let last_alloc = Arc::new(Mutex::new(0..0));
677 let listener_stream = PoolingStream {
678 inner: listener_stream,
679 pool: context.network_buffer_pool().clone(),
680 last_alloc: last_alloc.clone(),
681 };
682
683 let dialer_config = transport_config(dialer_crypto);
684 let listener_config = transport_config(listener_crypto.clone());
685
686 let listener_handle = context.child("listener").spawn(move |context| async move {
687 listen(
688 context,
689 |_| async { true },
690 listener_config,
691 listener_stream,
692 listener_sink,
693 )
694 .await
695 });
696
697 let (mut dialer_sender, _dialer_receiver) = dial(
698 context,
699 dialer_config,
700 listener_crypto.public_key(),
701 dialer_stream,
702 dialer_sink,
703 )
704 .await?;
705
706 let (_, _, mut listener_receiver) = listener_handle.await.unwrap()?;
707
708 dialer_sender.send(&b"hello"[..]).await?;
712 dialer_sender.send(&b"world"[..]).await?;
713
714 for expected in [&b"hello"[..], &b"world"[..]] {
715 let received = listener_receiver.recv().await?;
716 let plaintext = received.as_single().expect("single buffer expected");
717 let ptr = plaintext.as_ref().as_ptr() as usize;
718 assert!(
719 last_alloc.lock().contains(&ptr),
720 "plaintext should reuse the received frame buffer"
721 );
722 assert_eq!(plaintext.as_ref(), expected);
723 }
724 Ok(())
725 })
726 }
727
728 #[test]
729 fn test_send_many_uses_single_runtime_send() -> Result<(), Error> {
730 let executor = deterministic::Runner::default();
731 executor.start(|context| async move {
732 let dialer_crypto = PrivateKey::from_seed(42);
733 let listener_crypto = PrivateKey::from_seed(24);
734
735 let (dialer_sink, listener_stream) = mocks::Channel::init();
736 let (listener_sink, dialer_stream) = mocks::Channel::init();
737 let sends = Arc::new(AtomicUsize::new(0));
738 let chunk_counts = Arc::new(Mutex::new(Vec::new()));
739
740 let dialer_config = transport_config(dialer_crypto.clone());
741 let listener_config = transport_config(listener_crypto.clone());
742
743 let listener_handle = context.child("listener").spawn(move |context| async move {
744 listen(
745 context,
746 |_| async { true },
747 listener_config,
748 listener_stream,
749 listener_sink,
750 )
751 .await
752 });
753
754 let (mut dialer_sender, _dialer_receiver) = dial(
755 context,
756 dialer_config,
757 listener_crypto.public_key(),
758 dialer_stream,
759 CountingSink::new(dialer_sink, sends.clone(), chunk_counts.clone()),
760 )
761 .await?;
762
763 let (_listener_peer, _listener_sender, mut listener_receiver) =
764 listener_handle.await.unwrap()?;
765 sends.store(0, Ordering::Relaxed);
766 chunk_counts.lock().clear();
767
768 dialer_sender
771 .send_many(vec![
772 IoBufs::from(IoBuf::from(b"alpha")),
773 IoBufs::from(IoBuf::from(b"beta")),
774 IoBufs::from(IoBuf::from(b"gamma")),
775 ])
776 .await?;
777
778 assert_eq!(sends.load(Ordering::Relaxed), 1);
779 assert_eq!(*chunk_counts.lock(), vec![1]);
780 assert_eq!(
781 listener_receiver.recv().await?.coalesce(),
782 IoBuf::from(b"alpha")
783 );
784 assert_eq!(
785 listener_receiver.recv().await?.coalesce(),
786 IoBuf::from(b"beta")
787 );
788 assert_eq!(
789 listener_receiver.recv().await?.coalesce(),
790 IoBuf::from(b"gamma")
791 );
792 Ok(())
793 })
794 }
795
796 #[test]
797 fn test_send_many_flushes_at_network_pool_item_max() -> Result<(), Error> {
798 let executor = deterministic::Runner::new(
799 deterministic::Config::new().with_network_buffer_pool_config(
800 BufferPoolConfig::for_network()
801 .with_pool_min_size(256)
802 .with_min_size(NZUsize!(256))
803 .with_max_size(NZUsize!(256)),
804 ),
805 );
806 executor.start(|context| async move {
807 let dialer_crypto = PrivateKey::from_seed(42);
808 let listener_crypto = PrivateKey::from_seed(24);
809
810 let (dialer_sink, listener_stream) = mocks::Channel::init();
811 let (listener_sink, dialer_stream) = mocks::Channel::init();
812 let sends = Arc::new(AtomicUsize::new(0));
813 let chunk_counts = Arc::new(Mutex::new(Vec::new()));
814
815 let dialer_config = transport_config(dialer_crypto.clone());
816 let listener_config = transport_config(listener_crypto.clone());
817
818 let listener_handle = context.child("listener").spawn(move |context| async move {
819 listen(
820 context,
821 |_| async { true },
822 listener_config,
823 listener_stream,
824 listener_sink,
825 )
826 .await
827 });
828
829 let (mut dialer_sender, _dialer_receiver) = dial(
830 context,
831 dialer_config,
832 listener_crypto.public_key(),
833 dialer_stream,
834 CountingSink::new(dialer_sink, sends.clone(), chunk_counts.clone()),
835 )
836 .await?;
837
838 let (_listener_peer, _listener_sender, mut listener_receiver) =
839 listener_handle.await.unwrap()?;
840 sends.store(0, Ordering::Relaxed);
841 chunk_counts.lock().clear();
842
843 let payload = vec![7u8; 100];
847 dialer_sender
848 .send_many(vec![
849 IoBufs::from(IoBuf::from(payload.clone())),
850 IoBufs::from(IoBuf::from(payload.clone())),
851 IoBufs::from(IoBuf::from(payload.clone())),
852 ])
853 .await?;
854
855 assert_eq!(sends.load(Ordering::Relaxed), 1);
856 assert_eq!(*chunk_counts.lock(), vec![2]);
857 for _ in 0..3 {
858 assert_eq!(
859 listener_receiver.recv().await?.coalesce(),
860 payload.as_slice()
861 );
862 }
863 Ok(())
864 })
865 }
866
867 #[test]
868 fn test_send_many_sends_oversized_single_message_alone() -> Result<(), Error> {
869 let executor = deterministic::Runner::new(
870 deterministic::Config::new().with_network_buffer_pool_config(
871 BufferPoolConfig::for_network()
872 .with_pool_min_size(128)
873 .with_min_size(NZUsize!(128))
874 .with_max_size(NZUsize!(128)),
875 ),
876 );
877 executor.start(|context| async move {
878 let dialer_crypto = PrivateKey::from_seed(42);
879 let listener_crypto = PrivateKey::from_seed(24);
880
881 let (dialer_sink, listener_stream) = mocks::Channel::init();
882 let (listener_sink, dialer_stream) = mocks::Channel::init();
883 let sends = Arc::new(AtomicUsize::new(0));
884 let chunk_counts = Arc::new(Mutex::new(Vec::new()));
885
886 let dialer_config = transport_config(dialer_crypto.clone());
887 let listener_config = transport_config(listener_crypto.clone());
888
889 let listener_handle = context.child("listener").spawn(move |context| async move {
890 listen(
891 context,
892 |_| async { true },
893 listener_config,
894 listener_stream,
895 listener_sink,
896 )
897 .await
898 });
899
900 let (mut dialer_sender, _dialer_receiver) = dial(
901 context,
902 dialer_config,
903 listener_crypto.public_key(),
904 dialer_stream,
905 CountingSink::new(dialer_sink, sends.clone(), chunk_counts.clone()),
906 )
907 .await?;
908
909 let (_listener_peer, _listener_sender, mut listener_receiver) =
910 listener_handle.await.unwrap()?;
911 sends.store(0, Ordering::Relaxed);
912 chunk_counts.lock().clear();
913
914 let large = vec![3u8; 200];
917 let small = vec![9u8; 16];
918 dialer_sender
919 .send_many(vec![
920 IoBufs::from(IoBuf::from(large.clone())),
921 IoBufs::from(IoBuf::from(small.clone())),
922 ])
923 .await?;
924
925 assert_eq!(sends.load(Ordering::Relaxed), 1);
926 assert_eq!(*chunk_counts.lock(), vec![2]);
927 assert_eq!(listener_receiver.recv().await?.coalesce(), large.as_slice());
928 assert_eq!(listener_receiver.recv().await?.coalesce(), small.as_slice());
929 Ok(())
930 })
931 }
932
933 #[test]
934 fn test_send_many_too_large_preserves_sender_state() -> Result<(), Error> {
935 let executor = deterministic::Runner::default();
936 executor.start(|context| async move {
937 let dialer_crypto = PrivateKey::from_seed(42);
938 let listener_crypto = PrivateKey::from_seed(24);
939
940 let (dialer_sink, listener_stream) = mocks::Channel::init();
941 let (listener_sink, dialer_stream) = mocks::Channel::init();
942 let sends = Arc::new(AtomicUsize::new(0));
943 let chunk_counts = Arc::new(Mutex::new(Vec::new()));
944
945 let dialer_config = transport_config(dialer_crypto.clone());
946 let listener_config = transport_config(listener_crypto.clone());
947
948 let listener_handle = context.child("listener").spawn(move |context| async move {
949 listen(
950 context,
951 |_| async { true },
952 listener_config,
953 listener_stream,
954 listener_sink,
955 )
956 .await
957 });
958
959 let (mut dialer_sender, _dialer_receiver) = dial(
960 context,
961 dialer_config,
962 listener_crypto.public_key(),
963 dialer_stream,
964 CountingSink::new(dialer_sink, sends.clone(), chunk_counts.clone()),
965 )
966 .await?;
967
968 let (_listener_peer, _listener_sender, mut listener_receiver) =
969 listener_handle.await.unwrap()?;
970 sends.store(0, Ordering::Relaxed);
971 chunk_counts.lock().clear();
972
973 let valid = vec![7u8; 32];
974 let oversized = vec![9u8; MAX_MESSAGE_SIZE as usize + 1];
975 assert!(matches!(
976 dialer_sender
977 .send_many(vec![
978 IoBufs::from(IoBuf::from(valid)),
979 IoBufs::from(IoBuf::from(oversized)),
980 ])
981 .await,
982 Err(Error::SendTooLarge(_))
983 ));
984
985 assert_eq!(sends.load(Ordering::Relaxed), 0);
986 assert!(chunk_counts.lock().is_empty());
987
988 let recovered = b"recovered";
989 dialer_sender.send(&recovered[..]).await?;
990 assert_eq!(sends.load(Ordering::Relaxed), 1);
991 assert_eq!(listener_receiver.recv().await?.coalesce(), recovered);
992 Ok(())
993 })
994 }
995
996 #[test]
997 fn test_listen_rejects_oversized_fixed_size_peer_key_frame() {
998 let executor = deterministic::Runner::default();
999 executor.start(|context| async move {
1000 let dialer_crypto = PrivateKey::from_seed(42);
1001 let listener_crypto = PrivateKey::from_seed(24);
1002 let peer = dialer_crypto.public_key();
1003
1004 let (mut dialer_sink, listener_stream) = mocks::Channel::init();
1005 let (listener_sink, _dialer_stream) = mocks::Channel::init();
1006
1007 let mut listener_config = transport_config(listener_crypto);
1010 listener_config.max_message_size = 1024 * 1024;
1011
1012 dialer_sink
1016 .send(oversized_handshake_prefix(&peer))
1017 .await
1018 .unwrap();
1019
1020 let result = listen(
1021 context,
1022 |_| async { true },
1023 listener_config,
1024 listener_stream,
1025 listener_sink,
1026 )
1027 .await;
1028
1029 assert!(matches!(result, Err(Error::RecvTooLarge(n)) if n == peer.encode().len() + 1));
1033 });
1034 }
1035
1036 #[test]
1037 fn test_dial_rejects_oversized_fixed_size_syn_ack_frame() {
1038 let executor = deterministic::Runner::default();
1039 executor.start(|context| async move {
1040 let dialer_crypto = PrivateKey::from_seed(42);
1041 let listener_crypto = PrivateKey::from_seed(24);
1042
1043 let (dialer_sink, _listener_stream) = mocks::Channel::init();
1044 let (mut listener_sink, dialer_stream) = mocks::Channel::init();
1045
1046 let mut dialer_config = transport_config(dialer_crypto);
1049 dialer_config.max_message_size = 1024 * 1024;
1050
1051 let (current_time, ok_timestamps) = dialer_config.time_information(&context);
1054 let listener_public_key = listener_crypto.public_key();
1055 let dialer_public_key = dialer_config.signing_key.public_key();
1056 let (_, syn) = dial_start(
1057 context.child("dialer"),
1058 Context::new(
1059 &Transcript::new(&dialer_config.namespace),
1060 current_time,
1061 ok_timestamps.clone(),
1062 dialer_config.signing_key.clone(),
1063 listener_public_key.clone(),
1064 ),
1065 );
1066 let (_, syn_ack) = listen_start(
1067 context.child("listener"),
1068 Context::new(
1069 &Transcript::new(&dialer_config.namespace),
1070 current_time,
1071 ok_timestamps,
1072 listener_crypto,
1073 dialer_public_key,
1074 ),
1075 syn,
1076 )
1077 .expect("mock handshake should produce a valid syn_ack");
1078
1079 listener_sink
1082 .send(oversized_handshake_prefix(&syn_ack))
1083 .await
1084 .unwrap();
1085
1086 let result = dial(
1087 context,
1088 dialer_config,
1089 listener_public_key,
1090 dialer_stream,
1091 dialer_sink,
1092 )
1093 .await;
1094
1095 assert!(matches!(
1098 result,
1099 Err(Error::RecvTooLarge(n))
1100 if n == syn_ack.encode().len() + 1
1101 ));
1102 });
1103 }
1104}