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