1use std::collections::HashMap;
10use std::fs::File;
11use std::io::{Read, Write};
12use std::path::PathBuf;
13
14use super::compressor::Bzip2Compressor;
15use rns_core::channel::{Channel, Sequence};
16use rns_core::constants;
17use rns_core::link::types::{LinkId, LinkState, TeardownReason};
18use rns_core::link::{LinkAction, LinkEngine, LinkMode};
19use rns_core::packet::{PacketFlags, RawPacket};
20use rns_core::resource::{ResourceAction, ResourceReceiver, ResourceSender};
21use rns_crypto::ed25519::Ed25519PrivateKey;
22use rns_crypto::{OsRng, Rng};
23
24use super::time;
25use crate::resource::{
26 ReceivedResourceFile, ResourceReceiveMode, ResourceTransferError, ResourceTransferId,
27};
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum ResourceStrategy {
32 AcceptNone,
34 AcceptAll,
36 AcceptApp,
38}
39
40impl Default for ResourceStrategy {
41 fn default() -> Self {
42 ResourceStrategy::AcceptNone
43 }
44}
45
46struct ManagedLink {
48 engine: LinkEngine,
49 channel: Option<Channel>,
50 pending_channel_packets: HashMap<[u8; 32], Sequence>,
51 channel_send_ok: u64,
52 channel_send_not_ready: u64,
53 channel_send_too_big: u64,
54 channel_send_other_error: u64,
55 channel_messages_received: u64,
56 channel_proofs_sent: u64,
57 channel_proofs_received: u64,
58 dest_hash: [u8; 16],
60 remote_identity: Option<([u8; 16], [u8; 64])>,
62 dest_sig_pub_bytes: Option<[u8; 32]>,
64 incoming_resources: Vec<ResourceReceiver>,
66 outgoing_resources: Vec<ResourceSender>,
68 pending_requests: HashMap<[u8; 16], PendingRequest>,
70 incoming_splits: HashMap<[u8; 32], IncomingSplitTransfer>,
72 outgoing_splits: HashMap<[u8; 32], OutgoingSplitTransfer>,
74 outgoing_streams: HashMap<[u8; 32], OutgoingStreamTransfer>,
76 resource_strategy: ResourceStrategy,
78 resource_receive_mode: ResourceReceiveMode,
80 max_request_size: Option<usize>,
82 route_interface: Option<rns_core::transport::types::InterfaceId>,
84 route_transport_id: Option<[u8; 16]>,
89}
90
91struct IncomingSplitTransfer {
92 total_segments: u64,
93 completed_segments: u64,
94 current_segment_index: u64,
95 current_received_parts: usize,
96 current_total_parts: usize,
97 storage: IncomingSplitStorage,
98 metadata: Option<Vec<u8>>,
99 is_request: bool,
100 is_response: bool,
101}
102
103enum IncomingSplitStorage {
104 Memory(Vec<u8>),
105 File {
106 file: File,
107 path: PathBuf,
108 size: u64,
109 },
110}
111
112struct OutgoingSplitTransfer {
113 total_segments: u64,
114 completed_segments: u64,
115 current_segment_index: u64,
116 current_sent_parts: usize,
117 current_total_parts: usize,
118}
119
120struct OutgoingStreamTransfer {
121 transfer_id: ResourceTransferId,
122 reader: Box<dyn Read + Send>,
123 remaining: u64,
124 declared_length: u64,
125 metadata_overhead: u64,
126 auto_compress: bool,
127}
128
129struct PendingRequest {
130 deadline: Option<f64>,
131 max_response_size: Option<usize>,
132}
133
134struct LinkDestination {
136 sig_prv: Ed25519PrivateKey,
137 sig_pub_bytes: [u8; 32],
138 resource_strategy: ResourceStrategy,
139 max_request_size: Option<usize>,
140}
141
142#[derive(Debug, Clone, PartialEq, Eq)]
144pub enum RequestResponse {
145 Bytes(Vec<u8>),
147 Resource {
149 data: Vec<u8>,
150 metadata: Option<Vec<u8>>,
151 auto_compress: bool,
152 },
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum RequestFailure {
158 ResponseTooLarge { size: u64, maximum: usize },
160}
161
162impl From<Vec<u8>> for RequestResponse {
163 fn from(data: Vec<u8>) -> Self {
164 RequestResponse::Bytes(data)
165 }
166}
167
168struct RequestHandlerEntry {
170 path: String,
172 path_hash: [u8; 16],
174 allowed_list: Option<Vec<[u8; 16]>>,
176 handler: Box<
178 dyn Fn(LinkId, &str, &[u8], Option<&([u8; 16], [u8; 64])>) -> Option<RequestResponse>
179 + Send,
180 >,
181}
182
183struct DeferredRequestHandlerEntry {
184 path: String,
185 path_hash: [u8; 16],
186 allowed_list: Option<Vec<[u8; 16]>>,
187 handler: Box<dyn Fn(LinkId, &str, [u8; 16], &[u8], Option<&([u8; 16], [u8; 64])>) + Send>,
188}
189
190#[derive(Debug)]
192pub enum LinkManagerAction {
193 SendPacket {
195 raw: Vec<u8>,
196 dest_type: u8,
197 attached_interface: Option<rns_core::transport::types::InterfaceId>,
198 },
199 LinkEstablished {
201 link_id: LinkId,
202 dest_hash: [u8; 16],
203 hops: u8,
204 rebalanced_at: Option<f64>,
205 rtt: f64,
206 is_initiator: bool,
207 },
208 LinkClosed {
210 link_id: LinkId,
211 reason: Option<TeardownReason>,
212 },
213 RemoteIdentified {
215 link_id: LinkId,
216 identity_hash: [u8; 16],
217 public_key: [u8; 64],
218 },
219 RegisterLinkDest { link_id: LinkId },
221 DeregisterLinkDest { link_id: LinkId },
223 ManagementRequest {
226 link_id: LinkId,
227 path_hash: [u8; 16],
228 data: Vec<u8>,
230 request_id: [u8; 16],
232 remote_identity: Option<([u8; 16], [u8; 64])>,
233 },
234 ResourceReceived {
236 link_id: LinkId,
237 data: Vec<u8>,
238 metadata: Option<Vec<u8>>,
239 },
240 ResourceFileReceived {
242 link_id: LinkId,
243 resource: ReceivedResourceFile,
244 },
245 ResourceCompleted { link_id: LinkId },
247 ResourceFailed { link_id: LinkId, error: String },
249 ResourceProgress {
251 link_id: LinkId,
252 received: usize,
253 total: usize,
254 },
255 ResourceStreamCompleted {
256 link_id: LinkId,
257 transfer_id: ResourceTransferId,
258 },
259 ResourceStreamFailed {
260 link_id: LinkId,
261 transfer_id: ResourceTransferId,
262 error: ResourceTransferError,
263 },
264 ResourceStreamProgress {
265 link_id: LinkId,
266 transfer_id: ResourceTransferId,
267 transferred: u64,
268 total: u64,
269 },
270 ResourceAcceptQuery {
272 link_id: LinkId,
273 resource_hash: Vec<u8>,
274 transfer_size: u64,
275 has_metadata: bool,
276 },
277 ChannelMessageReceived {
279 link_id: LinkId,
280 msgtype: u16,
281 payload: Vec<u8>,
282 },
283 LinkDataReceived {
285 link_id: LinkId,
286 context: u8,
287 data: Vec<u8>,
288 },
289 ResponseReceived {
291 link_id: LinkId,
292 request_id: [u8; 16],
293 data: Vec<u8>,
294 metadata: Option<Vec<u8>>,
295 },
296 RequestFailed {
298 link_id: LinkId,
299 request_id: [u8; 16],
300 reason: RequestFailure,
301 },
302 LinkRequestReceived {
304 link_id: LinkId,
305 receiving_interface: rns_core::transport::types::InterfaceId,
306 },
307}
308
309pub struct LinkManager {
311 links: HashMap<LinkId, ManagedLink>,
312 link_destinations: HashMap<[u8; 16], LinkDestination>,
313 request_handlers: Vec<RequestHandlerEntry>,
314 deferred_request_handlers: Vec<DeferredRequestHandlerEntry>,
315 management_paths: Vec<[u8; 16]>,
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq)]
321pub struct LinkRouteHint {
322 pub interface: rns_core::transport::types::InterfaceId,
323 pub transport_id: Option<[u8; 16]>,
324}
325
326impl LinkManager {
327 fn resource_sdu_for_link(link: &ManagedLink) -> usize {
328 let mtu = link.engine.mtu() as usize;
331 let derived = mtu.saturating_sub(constants::HEADER_MAXSIZE + constants::IFAC_MIN_SIZE);
332 if derived > 0 {
333 derived
334 } else {
335 constants::RESOURCE_SDU
336 }
337 }
338
339 fn split_progress_parts(
340 segment_index: u64,
341 total_segments: u64,
342 current_done: usize,
343 current_total: usize,
344 sdu: usize,
345 ) -> (usize, usize) {
346 let max_parts_per_segment = constants::RESOURCE_MAX_EFFICIENT_SIZE.div_ceil(sdu.max(1));
347 let total = (total_segments as usize).saturating_mul(max_parts_per_segment);
348 let completed_segments = segment_index.saturating_sub(1) as usize;
349 let completed = completed_segments.saturating_mul(max_parts_per_segment);
350 let current = if current_total == 0 {
351 0
352 } else if current_total < max_parts_per_segment {
353 let scaled =
354 (current_done as f64) * (max_parts_per_segment as f64 / current_total as f64);
355 scaled.floor() as usize
356 } else {
357 current_done
358 };
359 (completed.saturating_add(current).min(total), total)
360 }
361
362 fn resource_hash_key(hash: &[u8]) -> Option<[u8; 32]> {
363 let mut key = [0u8; 32];
364 if hash.len() != key.len() {
365 return None;
366 }
367 key.copy_from_slice(hash);
368 Some(key)
369 }
370
371 fn incoming_storage(
372 mode: &ResourceReceiveMode,
373 original_hash: &[u8; 32],
374 ) -> std::io::Result<IncomingSplitStorage> {
375 match mode {
376 ResourceReceiveMode::Memory { .. } => Ok(IncomingSplitStorage::Memory(Vec::new())),
377 ResourceReceiveMode::TemporaryFile { directory, .. } => {
378 let (file, path) = crate::resource::create_receive_file(directory, original_hash)?;
379 Ok(IncomingSplitStorage::File {
380 file,
381 path,
382 size: 0,
383 })
384 }
385 }
386 }
387
388 fn incoming_split_progress(split: &IncomingSplitTransfer, sdu: usize) -> (usize, usize) {
389 Self::split_progress_parts(
390 split.current_segment_index,
391 split.total_segments,
392 split.current_received_parts,
393 split.current_total_parts,
394 sdu,
395 )
396 }
397
398 fn outgoing_split_progress(split: &OutgoingSplitTransfer, sdu: usize) -> (usize, usize) {
399 Self::split_progress_parts(
400 split.current_segment_index,
401 split.total_segments,
402 split.current_sent_parts,
403 split.current_total_parts,
404 sdu,
405 )
406 }
407
408 pub fn new() -> Self {
410 LinkManager {
411 links: HashMap::new(),
412 link_destinations: HashMap::new(),
413 request_handlers: Vec::new(),
414 deferred_request_handlers: Vec::new(),
415 management_paths: Vec::new(),
416 }
417 }
418
419 pub fn register_management_path(&mut self, path_hash: [u8; 16]) {
423 if !self.management_paths.contains(&path_hash) {
424 self.management_paths.push(path_hash);
425 }
426 }
427
428 pub fn get_derived_key(&self, link_id: &LinkId) -> Option<Vec<u8>> {
430 self.links
431 .get(link_id)
432 .and_then(|link| link.engine.derived_key().map(|dk| dk.to_vec()))
433 }
434
435 pub fn get_link_route_hint(&self, link_id: &LinkId) -> Option<LinkRouteHint> {
437 self.links.get(link_id).and_then(|link| {
438 link.route_interface.map(|interface| LinkRouteHint {
439 interface,
440 transport_id: link.route_transport_id,
441 })
442 })
443 }
444
445 pub fn set_link_route_hint(
447 &mut self,
448 link_id: &LinkId,
449 interface: rns_core::transport::types::InterfaceId,
450 transport_id: Option<[u8; 16]>,
451 ) -> bool {
452 let Some(link) = self.links.get_mut(link_id) else {
453 return false;
454 };
455 link.route_interface = Some(interface);
456 link.route_transport_id = transport_id;
457 true
458 }
459
460 pub fn register_link_destination(
462 &mut self,
463 dest_hash: [u8; 16],
464 sig_prv: Ed25519PrivateKey,
465 sig_pub_bytes: [u8; 32],
466 resource_strategy: ResourceStrategy,
467 ) {
468 self.link_destinations.insert(
469 dest_hash,
470 LinkDestination {
471 sig_prv,
472 sig_pub_bytes,
473 resource_strategy,
474 max_request_size: None,
475 },
476 );
477 }
478
479 pub fn set_link_destination_max_request_size(
484 &mut self,
485 dest_hash: &[u8; 16],
486 max_request_size: Option<usize>,
487 ) -> bool {
488 let Some(destination) = self.link_destinations.get_mut(dest_hash) else {
489 return false;
490 };
491 destination.max_request_size = max_request_size;
492 for link in self.links.values_mut() {
493 if link.dest_hash == *dest_hash && link.dest_sig_pub_bytes.is_none() {
494 link.max_request_size = max_request_size;
495 }
496 }
497 true
498 }
499
500 pub fn deregister_link_destination(&mut self, dest_hash: &[u8; 16]) {
502 self.link_destinations.remove(dest_hash);
503 }
504
505 pub fn register_request_handler<F>(
511 &mut self,
512 path: &str,
513 allowed_list: Option<Vec<[u8; 16]>>,
514 handler: F,
515 ) where
516 F: Fn(LinkId, &str, &[u8], Option<&([u8; 16], [u8; 64])>) -> Option<Vec<u8>>
517 + Send
518 + 'static,
519 {
520 let path_hash = compute_path_hash(path);
521 self.request_handlers.push(RequestHandlerEntry {
522 path: path.to_string(),
523 path_hash,
524 allowed_list,
525 handler: Box::new(move |link_id, p, data, remote| {
526 handler(link_id, p, data, remote).map(RequestResponse::Bytes)
527 }),
528 });
529 }
530
531 pub fn register_request_handler_response<F>(
533 &mut self,
534 path: &str,
535 allowed_list: Option<Vec<[u8; 16]>>,
536 handler: F,
537 ) where
538 F: Fn(LinkId, &str, &[u8], Option<&([u8; 16], [u8; 64])>) -> Option<RequestResponse>
539 + Send
540 + 'static,
541 {
542 let path_hash = compute_path_hash(path);
543 self.request_handlers.push(RequestHandlerEntry {
544 path: path.to_string(),
545 path_hash,
546 allowed_list,
547 handler: Box::new(handler),
548 });
549 }
550
551 pub fn register_deferred_request_handler<F>(
553 &mut self,
554 path: &str,
555 allowed_list: Option<Vec<[u8; 16]>>,
556 handler: F,
557 ) where
558 F: Fn(LinkId, &str, [u8; 16], &[u8], Option<&([u8; 16], [u8; 64])>) + Send + 'static,
559 {
560 self.deferred_request_handlers
561 .push(DeferredRequestHandlerEntry {
562 path: path.to_string(),
563 path_hash: compute_path_hash(path),
564 allowed_list,
565 handler: Box::new(handler),
566 });
567 }
568
569 pub fn create_link(
577 &mut self,
578 dest_hash: &[u8; 16],
579 dest_sig_pub_bytes: &[u8; 32],
580 hops: u8,
581 mtu: u32,
582 rng: &mut dyn Rng,
583 ) -> (LinkId, Vec<LinkManagerAction>) {
584 let mode = LinkMode::Aes256Cbc;
585 let (mut engine, request_data) =
586 LinkEngine::new_initiator(dest_hash, hops, mode, Some(mtu), time::now(), rng);
587
588 let flags = PacketFlags {
590 header_type: constants::HEADER_1,
591 context_flag: constants::FLAG_UNSET,
592 transport_type: constants::TRANSPORT_BROADCAST,
593 destination_type: constants::DESTINATION_SINGLE,
594 packet_type: constants::PACKET_TYPE_LINKREQUEST,
595 };
596
597 let packet = match RawPacket::pack(
598 flags,
599 0,
600 dest_hash,
601 None,
602 constants::CONTEXT_NONE,
603 &request_data,
604 ) {
605 Ok(p) => p,
606 Err(_) => {
607 return ([0u8; 16], Vec::new());
609 }
610 };
611
612 engine.set_link_id_from_hashable(&packet.get_hashable_part(), request_data.len());
613 let link_id = *engine.link_id();
614
615 engine.record_outbound_traffic(packet.data.len());
616 let managed = ManagedLink {
617 engine,
618 channel: None,
619 pending_channel_packets: HashMap::new(),
620 channel_send_ok: 0,
621 channel_send_not_ready: 0,
622 channel_send_too_big: 0,
623 channel_send_other_error: 0,
624 channel_messages_received: 0,
625 channel_proofs_sent: 0,
626 channel_proofs_received: 0,
627 dest_hash: *dest_hash,
628 remote_identity: None,
629 dest_sig_pub_bytes: Some(*dest_sig_pub_bytes),
630 incoming_resources: Vec::new(),
631 outgoing_resources: Vec::new(),
632 pending_requests: HashMap::new(),
633 incoming_splits: HashMap::new(),
634 outgoing_splits: HashMap::new(),
635 outgoing_streams: HashMap::new(),
636 resource_strategy: ResourceStrategy::default(),
637 resource_receive_mode: ResourceReceiveMode::default(),
638 max_request_size: None,
639 route_interface: None,
640 route_transport_id: None,
641 };
642 self.links.insert(link_id, managed);
643
644 let mut actions = Vec::new();
645 actions.push(LinkManagerAction::RegisterLinkDest { link_id });
647 actions.push(LinkManagerAction::SendPacket {
649 raw: packet.raw,
650 dest_type: constants::DESTINATION_LINK,
651 attached_interface: None,
652 });
653
654 (link_id, actions)
655 }
656
657 pub fn handle_local_delivery(
663 &mut self,
664 dest_hash: [u8; 16],
665 raw: &[u8],
666 packet_hash: [u8; 32],
667 receiving_interface: rns_core::transport::types::InterfaceId,
668 rng: &mut dyn Rng,
669 ) -> Vec<LinkManagerAction> {
670 let packet = match RawPacket::unpack(raw) {
671 Ok(p) => p,
672 Err(_) => return Vec::new(),
673 };
674
675 if packet.flags.packet_type != constants::PACKET_TYPE_LINKREQUEST {
678 if let Some(link) = self.links.get_mut(&dest_hash) {
679 link.engine.record_inbound_traffic(packet.data.len());
680 }
681 }
682
683 match packet.flags.packet_type {
684 constants::PACKET_TYPE_LINKREQUEST => {
685 self.handle_linkrequest(&dest_hash, &packet, receiving_interface, rng)
686 }
687 constants::PACKET_TYPE_PROOF if packet.context == constants::CONTEXT_LRPROOF => {
688 self.handle_lrproof(&dest_hash, &packet, receiving_interface, rng)
690 }
691 constants::PACKET_TYPE_PROOF if packet.context == constants::CONTEXT_RESOURCE_PRF => {
692 self.handle_resource_prf(&dest_hash, &packet.data, rng)
695 }
696 constants::PACKET_TYPE_PROOF => self.handle_link_proof(&dest_hash, &packet, rng),
697 constants::PACKET_TYPE_DATA => {
698 self.handle_link_data(&dest_hash, &packet, packet_hash, receiving_interface, rng)
699 }
700 _ => Vec::new(),
701 }
702 }
703
704 fn handle_linkrequest(
706 &mut self,
707 dest_hash: &[u8; 16],
708 packet: &RawPacket,
709 receiving_interface: rns_core::transport::types::InterfaceId,
710 rng: &mut dyn Rng,
711 ) -> Vec<LinkManagerAction> {
712 let ld = match self.link_destinations.get(dest_hash) {
714 Some(ld) => ld,
715 None => return Vec::new(),
716 };
717
718 let hashable = packet.get_hashable_part();
719 let now = time::now();
720
721 let (engine, lrproof_data) = match LinkEngine::new_responder(
723 &ld.sig_prv,
724 &ld.sig_pub_bytes,
725 &packet.data,
726 &hashable,
727 dest_hash,
728 packet.hops,
729 now,
730 rng,
731 ) {
732 Ok(r) => r,
733 Err(e) => {
734 log::debug!("LINKREQUEST rejected: {}", e);
735 return Vec::new();
736 }
737 };
738
739 let link_id = *engine.link_id();
740 log::debug!(
741 "LINKREQUEST accepted: link={:02x?} iface={} header_type={} transport_id_present={} hops={}",
742 &link_id[..4],
743 receiving_interface.0,
744 packet.flags.header_type,
745 packet.transport_id.is_some(),
746 packet.hops
747 );
748
749 let managed = ManagedLink {
750 engine,
751 channel: None,
752 pending_channel_packets: HashMap::new(),
753 channel_send_ok: 0,
754 channel_send_not_ready: 0,
755 channel_send_too_big: 0,
756 channel_send_other_error: 0,
757 channel_messages_received: 0,
758 channel_proofs_sent: 0,
759 channel_proofs_received: 0,
760 dest_hash: *dest_hash,
761 remote_identity: None,
762 dest_sig_pub_bytes: None,
763 incoming_resources: Vec::new(),
764 outgoing_resources: Vec::new(),
765 pending_requests: HashMap::new(),
766 incoming_splits: HashMap::new(),
767 outgoing_splits: HashMap::new(),
768 outgoing_streams: HashMap::new(),
769 resource_strategy: ld.resource_strategy,
770 resource_receive_mode: ResourceReceiveMode::default(),
771 max_request_size: ld.max_request_size,
772 route_interface: Some(receiving_interface),
773 route_transport_id: if packet.flags.header_type == constants::HEADER_2 {
774 packet.transport_id
775 } else {
776 None
777 },
778 };
779 self.links.insert(link_id, managed);
780
781 let flags = PacketFlags {
783 header_type: constants::HEADER_1,
784 context_flag: constants::FLAG_UNSET,
785 transport_type: constants::TRANSPORT_BROADCAST,
786 destination_type: constants::DESTINATION_LINK,
787 packet_type: constants::PACKET_TYPE_PROOF,
788 };
789
790 let mut actions = Vec::new();
791
792 actions.push(LinkManagerAction::RegisterLinkDest { link_id });
794
795 if let Ok((raw, _packet_hash)) = RawPacket::pack_raw_with_hash(
796 flags,
797 0,
798 &link_id,
799 None,
800 constants::CONTEXT_LRPROOF,
801 &lrproof_data,
802 ) {
803 log::debug!(
804 "LRPROOF queued: link={:02x?} route_iface={} route_tid_present={} hops=0",
805 &link_id[..4],
806 receiving_interface.0,
807 packet.transport_id.is_some()
808 );
809 actions.push(LinkManagerAction::SendPacket {
810 raw,
811 dest_type: constants::DESTINATION_LINK,
812 attached_interface: None,
813 });
814 }
815
816 actions.push(LinkManagerAction::LinkRequestReceived {
818 link_id,
819 receiving_interface,
820 });
821
822 actions
823 }
824
825 fn handle_link_proof(
826 &mut self,
827 link_id: &LinkId,
828 packet: &RawPacket,
829 rng: &mut dyn Rng,
830 ) -> Vec<LinkManagerAction> {
831 if packet.data.len() < 32 {
832 return Vec::new();
833 }
834
835 let mut tracked_hash = [0u8; 32];
836 tracked_hash.copy_from_slice(&packet.data[..32]);
837
838 let Some(link) = self.links.get_mut(link_id) else {
839 return Vec::new();
840 };
841 let Some(sequence) = link.pending_channel_packets.remove(&tracked_hash) else {
842 return Vec::new();
843 };
844 link.channel_proofs_received += 1;
845 let Some(channel) = link.channel.as_mut() else {
846 return Vec::new();
847 };
848
849 let chan_actions = channel.packet_delivered(sequence);
850 let _ = channel;
851 let _ = link;
852 self.process_channel_actions(link_id, chan_actions, rng)
853 }
854
855 fn build_link_packet_proof(
856 &mut self,
857 link_id: &LinkId,
858 packet_hash: &[u8; 32],
859 ) -> Vec<LinkManagerAction> {
860 let dest_hash = match self.links.get(link_id) {
861 Some(link) => link.dest_hash,
862 None => return Vec::new(),
863 };
864 let Some(ld) = self.link_destinations.get(&dest_hash) else {
865 return Vec::new();
866 };
867 if let Some(link) = self.links.get_mut(link_id) {
868 link.channel_proofs_sent += 1;
869 }
870
871 let signature = ld.sig_prv.sign(packet_hash);
872 let mut proof_data = Vec::with_capacity(96);
873 proof_data.extend_from_slice(packet_hash);
874 proof_data.extend_from_slice(&signature);
875
876 let flags = PacketFlags {
877 header_type: constants::HEADER_1,
878 context_flag: constants::FLAG_UNSET,
879 transport_type: constants::TRANSPORT_BROADCAST,
880 destination_type: constants::DESTINATION_LINK,
881 packet_type: constants::PACKET_TYPE_PROOF,
882 };
883 if let Ok((raw, _packet_hash)) = RawPacket::pack_raw_with_hash(
884 flags,
885 0,
886 link_id,
887 None,
888 constants::CONTEXT_NONE,
889 &proof_data,
890 ) {
891 vec![LinkManagerAction::SendPacket {
892 raw,
893 dest_type: constants::DESTINATION_LINK,
894 attached_interface: None,
895 }]
896 } else {
897 Vec::new()
898 }
899 }
900
901 fn handle_lrproof(
903 &mut self,
904 link_id_bytes: &[u8; 16],
905 packet: &RawPacket,
906 receiving_interface: rns_core::transport::types::InterfaceId,
907 rng: &mut dyn Rng,
908 ) -> Vec<LinkManagerAction> {
909 let link = match self.links.get_mut(link_id_bytes) {
910 Some(l) => l,
911 None => return Vec::new(),
912 };
913
914 link.route_interface = Some(receiving_interface);
915 if packet.flags.header_type == constants::HEADER_2 {
916 if let Some(transport_id) = packet.transport_id {
917 link.route_transport_id = Some(transport_id);
918 }
919 }
920 log::debug!(
921 "LRPROOF received: link={:02x?} iface={} header_type={} transport_id_present={}",
922 &link_id_bytes[..4],
923 receiving_interface.0,
924 packet.flags.header_type,
925 packet.transport_id.is_some()
926 );
927
928 if link.engine.state() != LinkState::Pending || !link.engine.is_initiator() {
929 return Vec::new();
930 }
931
932 let dest_sig_pub_bytes = match link.dest_sig_pub_bytes {
934 Some(b) => b,
935 None => {
936 log::debug!("LRPROOF: no destination signing key available");
937 return Vec::new();
938 }
939 };
940
941 let now = time::now();
942 let (lrrtt_encrypted, link_actions) = match link.engine.handle_lrproof_with_hops(
943 &packet.data,
944 &dest_sig_pub_bytes,
945 Some(packet.hops),
946 now,
947 rng,
948 ) {
949 Ok(r) => r,
950 Err(e) => {
951 log::debug!("LRPROOF validation failed: {}", e);
952 return Vec::new();
953 }
954 };
955
956 let link_id = *link.engine.link_id();
957 let mut actions = Vec::new();
958
959 actions.extend(self.process_link_actions(&link_id, &link_actions));
961
962 let flags = PacketFlags {
964 header_type: constants::HEADER_1,
965 context_flag: constants::FLAG_UNSET,
966 transport_type: constants::TRANSPORT_BROADCAST,
967 destination_type: constants::DESTINATION_LINK,
968 packet_type: constants::PACKET_TYPE_DATA,
969 };
970
971 if let Ok((raw, _packet_hash)) = RawPacket::pack_raw_with_hash(
972 flags,
973 0,
974 &link_id,
975 None,
976 constants::CONTEXT_LRRTT,
977 &lrrtt_encrypted,
978 ) {
979 actions.push(LinkManagerAction::SendPacket {
980 raw,
981 dest_type: constants::DESTINATION_LINK,
982 attached_interface: None,
983 });
984 }
985
986 if let Some(link) = self.links.get_mut(&link_id) {
988 if link.engine.state() == LinkState::Active {
989 let rtt = link.engine.rtt().unwrap_or(1.0);
990 link.channel = Some(Channel::new(rtt));
991 }
992 }
993
994 actions
995 }
996
997 fn handle_link_data(
1003 &mut self,
1004 link_id_bytes: &[u8; 16],
1005 packet: &RawPacket,
1006 packet_hash: [u8; 32],
1007 receiving_interface: rns_core::transport::types::InterfaceId,
1008 rng: &mut dyn Rng,
1009 ) -> Vec<LinkManagerAction> {
1010 enum LinkDataResult<'a> {
1012 Lrrtt {
1013 link_id: LinkId,
1014 link_actions: Vec<LinkAction>,
1015 },
1016 Identify {
1017 link_id: LinkId,
1018 link_actions: Vec<LinkAction>,
1019 },
1020 Keepalive {
1021 link_id: LinkId,
1022 inbound_actions: Vec<LinkAction>,
1023 reply: bool,
1024 received_at: f64,
1025 },
1026 LinkClose {
1027 link_id: LinkId,
1028 teardown_actions: Vec<LinkAction>,
1029 },
1030 Channel {
1031 link_id: LinkId,
1032 inbound_actions: Vec<LinkAction>,
1033 plaintext: Vec<u8>,
1034 packet_hash: [u8; 32],
1035 },
1036 Request {
1037 link_id: LinkId,
1038 inbound_actions: Vec<LinkAction>,
1039 plaintext: Vec<u8>,
1040 request_id: [u8; 16],
1041 },
1042 Response {
1043 link_id: LinkId,
1044 inbound_actions: Vec<LinkAction>,
1045 plaintext: Vec<u8>,
1046 },
1047 Generic {
1048 link_id: LinkId,
1049 inbound_actions: Vec<LinkAction>,
1050 plaintext: Vec<u8>,
1051 context: u8,
1052 packet_hash: [u8; 32],
1053 },
1054 ResourceAdv {
1056 link_id: LinkId,
1057 inbound_actions: Vec<LinkAction>,
1058 plaintext: Vec<u8>,
1059 },
1060 ResourceReq {
1062 link_id: LinkId,
1063 inbound_actions: Vec<LinkAction>,
1064 plaintext: Vec<u8>,
1065 },
1066 ResourceHmu {
1068 link_id: LinkId,
1069 inbound_actions: Vec<LinkAction>,
1070 plaintext: Vec<u8>,
1071 },
1072 ResourcePart {
1074 link_id: LinkId,
1075 inbound_actions: Vec<LinkAction>,
1076 raw_data: &'a [u8],
1077 },
1078 ResourcePrf {
1080 link_id: LinkId,
1081 inbound_actions: Vec<LinkAction>,
1082 plaintext: Vec<u8>,
1083 },
1084 ResourceIcl {
1086 link_id: LinkId,
1087 inbound_actions: Vec<LinkAction>,
1088 },
1089 ResourceRcl {
1091 link_id: LinkId,
1092 inbound_actions: Vec<LinkAction>,
1093 },
1094 Error,
1095 }
1096
1097 let result = {
1098 let link = match self.links.get_mut(link_id_bytes) {
1099 Some(l) => l,
1100 None => return Vec::new(),
1101 };
1102
1103 link.route_interface = Some(receiving_interface);
1104 if packet.flags.header_type == constants::HEADER_2 {
1105 if let Some(transport_id) = packet.transport_id {
1106 link.route_transport_id = Some(transport_id);
1107 }
1108 } else {
1109 link.route_transport_id = None;
1110 }
1111
1112 match packet.context {
1113 constants::CONTEXT_LRRTT => {
1114 match link.engine.handle_lrrtt_with_hops(
1115 &packet.data,
1116 Some(packet.hops),
1117 time::now(),
1118 ) {
1119 Ok(link_actions) => {
1120 let link_id = *link.engine.link_id();
1121 LinkDataResult::Lrrtt {
1122 link_id,
1123 link_actions,
1124 }
1125 }
1126 Err(e) => {
1127 log::debug!("LRRTT handling failed: {}", e);
1128 LinkDataResult::Error
1129 }
1130 }
1131 }
1132 constants::CONTEXT_LINKIDENTIFY => {
1133 match link.engine.handle_identify(&packet.data) {
1134 Ok(link_actions) => {
1135 let link_id = *link.engine.link_id();
1136 link.remote_identity = link.engine.remote_identity().cloned();
1137 LinkDataResult::Identify {
1138 link_id,
1139 link_actions,
1140 }
1141 }
1142 Err(e) => {
1143 log::debug!("LINKIDENTIFY failed: {}", e);
1144 LinkDataResult::Error
1145 }
1146 }
1147 }
1148 constants::CONTEXT_KEEPALIVE => {
1149 let received_at = time::now();
1150 let reply = link
1151 .engine
1152 .should_reply_keepalive(&packet.data, received_at);
1153 let inbound_actions = link.engine.record_inbound(received_at);
1154 let link_id = *link.engine.link_id();
1155 LinkDataResult::Keepalive {
1156 link_id,
1157 inbound_actions,
1158 reply,
1159 received_at,
1160 }
1161 }
1162 constants::CONTEXT_LINKCLOSE => match link.engine.decrypt(&packet.data) {
1163 Ok(plaintext) if plaintext.as_slice() == link_id_bytes => {
1164 let teardown_actions = link.engine.handle_teardown();
1165 let link_id = *link.engine.link_id();
1166 LinkDataResult::LinkClose {
1167 link_id,
1168 teardown_actions,
1169 }
1170 }
1171 _ => LinkDataResult::Error,
1172 },
1173 constants::CONTEXT_CHANNEL => match link.engine.decrypt(&packet.data) {
1174 Ok(plaintext) => {
1175 let inbound_actions = link.engine.record_inbound(time::now());
1176 let link_id = *link.engine.link_id();
1177 LinkDataResult::Channel {
1178 link_id,
1179 inbound_actions,
1180 plaintext,
1181 packet_hash,
1182 }
1183 }
1184 Err(_) => LinkDataResult::Error,
1185 },
1186 constants::CONTEXT_REQUEST => match link.engine.decrypt(&packet.data) {
1187 Ok(plaintext) => {
1188 let inbound_actions = link.engine.record_inbound(time::now());
1189 let link_id = *link.engine.link_id();
1190 let request_id = packet.get_truncated_hash();
1191 LinkDataResult::Request {
1192 link_id,
1193 inbound_actions,
1194 plaintext,
1195 request_id,
1196 }
1197 }
1198 Err(_) => LinkDataResult::Error,
1199 },
1200 constants::CONTEXT_RESPONSE => match link.engine.decrypt(&packet.data) {
1201 Ok(plaintext) => {
1202 let inbound_actions = link.engine.record_inbound(time::now());
1203 let link_id = *link.engine.link_id();
1204 LinkDataResult::Response {
1205 link_id,
1206 inbound_actions,
1207 plaintext,
1208 }
1209 }
1210 Err(_) => LinkDataResult::Error,
1211 },
1212 constants::CONTEXT_RESOURCE_ADV => match link.engine.decrypt(&packet.data) {
1214 Ok(plaintext) => {
1215 let inbound_actions = link.engine.record_inbound(time::now());
1216 let link_id = *link.engine.link_id();
1217 LinkDataResult::ResourceAdv {
1218 link_id,
1219 inbound_actions,
1220 plaintext,
1221 }
1222 }
1223 Err(_) => LinkDataResult::Error,
1224 },
1225 constants::CONTEXT_RESOURCE_REQ => match link.engine.decrypt(&packet.data) {
1226 Ok(plaintext) => {
1227 let inbound_actions = link.engine.record_inbound(time::now());
1228 let link_id = *link.engine.link_id();
1229 LinkDataResult::ResourceReq {
1230 link_id,
1231 inbound_actions,
1232 plaintext,
1233 }
1234 }
1235 Err(_) => LinkDataResult::Error,
1236 },
1237 constants::CONTEXT_RESOURCE_HMU => match link.engine.decrypt(&packet.data) {
1238 Ok(plaintext) => {
1239 let inbound_actions = link.engine.record_inbound(time::now());
1240 let link_id = *link.engine.link_id();
1241 LinkDataResult::ResourceHmu {
1242 link_id,
1243 inbound_actions,
1244 plaintext,
1245 }
1246 }
1247 Err(_) => LinkDataResult::Error,
1248 },
1249 constants::CONTEXT_RESOURCE => {
1250 let inbound_actions = link.engine.record_inbound(time::now());
1252 let link_id = *link.engine.link_id();
1253 LinkDataResult::ResourcePart {
1254 link_id,
1255 inbound_actions,
1256 raw_data: &packet.data,
1257 }
1258 }
1259 constants::CONTEXT_RESOURCE_PRF => match link.engine.decrypt(&packet.data) {
1260 Ok(plaintext) => {
1261 let inbound_actions = link.engine.record_inbound(time::now());
1262 let link_id = *link.engine.link_id();
1263 LinkDataResult::ResourcePrf {
1264 link_id,
1265 inbound_actions,
1266 plaintext,
1267 }
1268 }
1269 Err(_) => LinkDataResult::Error,
1270 },
1271 constants::CONTEXT_RESOURCE_ICL => {
1272 let _ = link.engine.decrypt(&packet.data); let inbound_actions = link.engine.record_inbound(time::now());
1274 let link_id = *link.engine.link_id();
1275 LinkDataResult::ResourceIcl {
1276 link_id,
1277 inbound_actions,
1278 }
1279 }
1280 constants::CONTEXT_RESOURCE_RCL => {
1281 let _ = link.engine.decrypt(&packet.data); let inbound_actions = link.engine.record_inbound(time::now());
1283 let link_id = *link.engine.link_id();
1284 LinkDataResult::ResourceRcl {
1285 link_id,
1286 inbound_actions,
1287 }
1288 }
1289 _ => match link.engine.decrypt(&packet.data) {
1290 Ok(plaintext) => {
1291 let inbound_actions = link.engine.record_inbound(time::now());
1292 let link_id = *link.engine.link_id();
1293 LinkDataResult::Generic {
1294 link_id,
1295 inbound_actions,
1296 plaintext,
1297 context: packet.context,
1298 packet_hash,
1299 }
1300 }
1301 Err(_) => LinkDataResult::Error,
1302 },
1303 }
1304 }; let mut actions = Vec::new();
1308 match result {
1309 LinkDataResult::Lrrtt {
1310 link_id,
1311 link_actions,
1312 } => {
1313 actions.extend(self.process_link_actions(&link_id, &link_actions));
1314 if let Some(link) = self.links.get_mut(&link_id) {
1316 if link.engine.state() == LinkState::Active {
1317 let rtt = link.engine.rtt().unwrap_or(1.0);
1318 link.channel = Some(Channel::new(rtt));
1319 }
1320 }
1321 }
1322 LinkDataResult::Identify {
1323 link_id,
1324 link_actions,
1325 } => {
1326 actions.extend(self.process_link_actions(&link_id, &link_actions));
1327 }
1328 LinkDataResult::Keepalive {
1329 link_id,
1330 inbound_actions,
1331 reply,
1332 received_at,
1333 } => {
1334 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
1335 if reply {
1336 let flags = PacketFlags {
1337 header_type: constants::HEADER_1,
1338 context_flag: constants::FLAG_UNSET,
1339 transport_type: constants::TRANSPORT_BROADCAST,
1340 destination_type: constants::DESTINATION_LINK,
1341 packet_type: constants::PACKET_TYPE_DATA,
1342 };
1343 if let Ok((raw, _)) = RawPacket::pack_raw_with_hash(
1344 flags,
1345 0,
1346 &link_id,
1347 None,
1348 constants::CONTEXT_KEEPALIVE,
1349 &[0xfe],
1350 ) {
1351 actions.push(LinkManagerAction::SendPacket {
1352 raw,
1353 dest_type: constants::DESTINATION_LINK,
1354 attached_interface: None,
1355 });
1356 if let Some(link) = self.links.get_mut(&link_id) {
1357 link.engine.record_outbound(received_at, true);
1358 }
1359 }
1360 }
1361 }
1362 LinkDataResult::LinkClose {
1363 link_id,
1364 teardown_actions,
1365 } => {
1366 actions.extend(self.process_link_actions(&link_id, &teardown_actions));
1367 }
1368 LinkDataResult::Channel {
1369 link_id,
1370 inbound_actions,
1371 plaintext,
1372 packet_hash,
1373 } => {
1374 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
1375 if let Some(link) = self.links.get_mut(&link_id) {
1377 if let Some(ref mut channel) = link.channel {
1378 let chan_actions = channel.receive(&plaintext, time::now());
1379 link.channel_messages_received += chan_actions
1380 .iter()
1381 .filter(|action| {
1382 matches!(
1383 action,
1384 rns_core::channel::ChannelAction::MessageReceived { .. }
1385 )
1386 })
1387 .count()
1388 as u64;
1389 let _ = link;
1391 actions.extend(self.process_channel_actions(&link_id, chan_actions, rng));
1392 }
1393 }
1394 actions.extend(self.build_link_packet_proof(&link_id, &packet_hash));
1395 }
1396 LinkDataResult::Request {
1397 link_id,
1398 inbound_actions,
1399 plaintext,
1400 request_id,
1401 } => {
1402 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
1403 let size_ok = self
1404 .links
1405 .get(&link_id)
1406 .and_then(|link| link.max_request_size)
1407 .is_none_or(|limit| plaintext.len() <= limit);
1408 if size_ok {
1409 actions.extend(self.handle_request(&link_id, &plaintext, request_id, rng));
1410 } else {
1411 log::debug!(
1412 "ignored request with excessive size {} bytes on link {:02x?}",
1413 plaintext.len(),
1414 &link_id[..4]
1415 );
1416 }
1417 }
1418 LinkDataResult::Response {
1419 link_id,
1420 inbound_actions,
1421 plaintext,
1422 } => {
1423 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
1424 actions.extend(self.handle_response(&link_id, &plaintext, None, None));
1426 }
1427 LinkDataResult::Generic {
1428 link_id,
1429 inbound_actions,
1430 plaintext,
1431 context,
1432 packet_hash,
1433 } => {
1434 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
1435 actions.push(LinkManagerAction::LinkDataReceived {
1436 link_id,
1437 context,
1438 data: plaintext,
1439 });
1440
1441 actions.extend(self.build_link_packet_proof(&link_id, &packet_hash));
1442 }
1443 LinkDataResult::ResourceAdv {
1444 link_id,
1445 inbound_actions,
1446 plaintext,
1447 } => {
1448 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
1449 actions.extend(self.handle_resource_adv(&link_id, &plaintext, rng));
1450 }
1451 LinkDataResult::ResourceReq {
1452 link_id,
1453 inbound_actions,
1454 plaintext,
1455 } => {
1456 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
1457 actions.extend(self.handle_resource_req(&link_id, &plaintext, rng));
1458 }
1459 LinkDataResult::ResourceHmu {
1460 link_id,
1461 inbound_actions,
1462 plaintext,
1463 } => {
1464 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
1465 actions.extend(self.handle_resource_hmu(&link_id, &plaintext, rng));
1466 }
1467 LinkDataResult::ResourcePart {
1468 link_id,
1469 inbound_actions,
1470 raw_data,
1471 } => {
1472 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
1473 actions.extend(self.handle_resource_part(&link_id, &raw_data, rng));
1474 }
1475 LinkDataResult::ResourcePrf {
1476 link_id,
1477 inbound_actions,
1478 plaintext,
1479 } => {
1480 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
1481 actions.extend(self.handle_resource_prf(&link_id, &plaintext, rng));
1482 }
1483 LinkDataResult::ResourceIcl {
1484 link_id,
1485 inbound_actions,
1486 } => {
1487 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
1488 actions.extend(self.handle_resource_icl(&link_id));
1489 }
1490 LinkDataResult::ResourceRcl {
1491 link_id,
1492 inbound_actions,
1493 } => {
1494 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
1495 actions.extend(self.handle_resource_rcl(&link_id));
1496 }
1497 LinkDataResult::Error => {}
1498 }
1499
1500 actions
1501 }
1502
1503 fn handle_request(
1505 &mut self,
1506 link_id: &LinkId,
1507 plaintext: &[u8],
1508 request_id: [u8; 16],
1509 rng: &mut dyn Rng,
1510 ) -> Vec<LinkManagerAction> {
1511 use rns_core::msgpack::{self, Value};
1512
1513 let arr = match msgpack::unpack_exact(plaintext) {
1515 Ok(Value::Array(arr)) if arr.len() >= 3 => arr,
1516 _ => return Vec::new(),
1517 };
1518
1519 let path_hash_bytes = match &arr[1] {
1520 Value::Bin(b) if b.len() == 16 => b,
1521 _ => return Vec::new(),
1522 };
1523 let mut path_hash = [0u8; 16];
1524 path_hash.copy_from_slice(path_hash_bytes);
1525
1526 let request_data = msgpack::pack(&arr[2]);
1528
1529 if self.management_paths.contains(&path_hash) {
1531 let remote_identity = self
1532 .links
1533 .get(link_id)
1534 .and_then(|l| l.remote_identity)
1535 .map(|(h, k)| (h, k));
1536 return vec![LinkManagerAction::ManagementRequest {
1537 link_id: *link_id,
1538 path_hash,
1539 data: request_data,
1540 request_id,
1541 remote_identity,
1542 }];
1543 }
1544
1545 if let Some(handler) = self
1546 .deferred_request_handlers
1547 .iter()
1548 .find(|handler| handler.path_hash == path_hash)
1549 {
1550 let remote_identity = self
1551 .links
1552 .get(link_id)
1553 .and_then(|link| link.remote_identity.as_ref());
1554 if let Some(allowed) = &handler.allowed_list {
1555 let Some((identity_hash, _)) = remote_identity else {
1556 log::debug!("Deferred request denied: peer not identified");
1557 return Vec::new();
1558 };
1559 if !allowed.contains(identity_hash) {
1560 log::debug!("Deferred request denied: identity not in allowed list");
1561 return Vec::new();
1562 }
1563 }
1564 (handler.handler)(
1565 *link_id,
1566 &handler.path,
1567 request_id,
1568 &request_data,
1569 remote_identity,
1570 );
1571 return Vec::new();
1572 }
1573
1574 let handler_idx = self
1576 .request_handlers
1577 .iter()
1578 .position(|h| h.path_hash == path_hash);
1579 let handler_idx = match handler_idx {
1580 Some(i) => i,
1581 None => return Vec::new(),
1582 };
1583
1584 let remote_identity = self
1586 .links
1587 .get(link_id)
1588 .and_then(|l| l.remote_identity.as_ref());
1589 let handler = &self.request_handlers[handler_idx];
1590 if let Some(ref allowed) = handler.allowed_list {
1591 match remote_identity {
1592 Some((identity_hash, _)) => {
1593 if !allowed.contains(identity_hash) {
1594 log::debug!("Request denied: identity not in allowed list");
1595 return Vec::new();
1596 }
1597 }
1598 None => {
1599 log::debug!("Request denied: peer not identified");
1600 return Vec::new();
1601 }
1602 }
1603 }
1604
1605 let path = handler.path.clone();
1607 let response = (handler.handler)(*link_id, &path, &request_data, remote_identity);
1608
1609 let mut actions = Vec::new();
1610 if let Some(response) = response {
1611 match response {
1612 RequestResponse::Bytes(response_data) => {
1613 let mut response_actions =
1614 self.build_response_packet(link_id, &request_id, &response_data, rng);
1615 if response_actions.is_empty() {
1616 response_actions.extend(self.send_response_resource(
1617 link_id,
1618 &request_id,
1619 &response_data,
1620 None,
1621 true,
1622 rng,
1623 ));
1624 }
1625 actions.extend(response_actions);
1626 }
1627 RequestResponse::Resource {
1628 data,
1629 metadata,
1630 auto_compress,
1631 } => {
1632 actions.extend(self.send_response_resource(
1633 link_id,
1634 &request_id,
1635 &data,
1636 metadata.as_deref(),
1637 auto_compress,
1638 rng,
1639 ));
1640 }
1641 }
1642 }
1643
1644 actions
1645 }
1646
1647 fn build_response_packet(
1650 &self,
1651 link_id: &LinkId,
1652 request_id: &[u8; 16],
1653 response_data: &[u8],
1654 rng: &mut dyn Rng,
1655 ) -> Vec<LinkManagerAction> {
1656 use rns_core::msgpack::{self, Value};
1657
1658 let response_value = msgpack::unpack_exact(response_data)
1659 .unwrap_or_else(|_| Value::Bin(response_data.to_vec()));
1660
1661 let response_array = Value::Array(vec![Value::Bin(request_id.to_vec()), response_value]);
1662 let response_plaintext = msgpack::pack(&response_array);
1663
1664 let mut actions = Vec::new();
1665 if let Some(link) = self.links.get(link_id) {
1666 if let Ok(encrypted) = link.engine.encrypt(&response_plaintext, rng) {
1667 let flags = PacketFlags {
1668 header_type: constants::HEADER_1,
1669 context_flag: constants::FLAG_UNSET,
1670 transport_type: constants::TRANSPORT_BROADCAST,
1671 destination_type: constants::DESTINATION_LINK,
1672 packet_type: constants::PACKET_TYPE_DATA,
1673 };
1674 let max_mtu = link.engine.mtu() as usize;
1675 if let Ok((raw, _packet_hash)) = RawPacket::pack_raw_with_hash_with_max_mtu(
1676 flags,
1677 0,
1678 link_id,
1679 None,
1680 constants::CONTEXT_RESPONSE,
1681 &encrypted,
1682 max_mtu,
1683 ) {
1684 actions.push(LinkManagerAction::SendPacket {
1685 raw,
1686 dest_type: constants::DESTINATION_LINK,
1687 attached_interface: None,
1688 });
1689 }
1690 }
1691 }
1692 actions
1693 }
1694
1695 fn send_response_resource(
1696 &mut self,
1697 link_id: &LinkId,
1698 request_id: &[u8; 16],
1699 response_data: &[u8],
1700 metadata: Option<&[u8]>,
1701 auto_compress: bool,
1702 rng: &mut dyn Rng,
1703 ) -> Vec<LinkManagerAction> {
1704 use rns_core::msgpack::{self, Value};
1705
1706 let link = match self.links.get_mut(link_id) {
1707 Some(l) => l,
1708 None => return Vec::new(),
1709 };
1710
1711 if link.engine.state() != LinkState::Active {
1712 return Vec::new();
1713 }
1714
1715 let now = time::now();
1716
1717 let response_value = msgpack::unpack_exact(response_data)
1721 .unwrap_or_else(|_| Value::Bin(response_data.to_vec()));
1722 let response_array = Value::Array(vec![Value::Bin(request_id.to_vec()), response_value]);
1723 let resource_payload = msgpack::pack(&response_array);
1724
1725 let senders = match Self::build_resource_senders(
1726 link,
1727 &resource_payload,
1728 metadata,
1729 auto_compress,
1730 true, Some(request_id.to_vec()),
1732 rng,
1733 now,
1734 ) {
1735 Ok(s) => s,
1736 Err(e) => {
1737 log::debug!("Failed to create response ResourceSender: {}", e);
1738 return Vec::new();
1739 }
1740 };
1741
1742 let adv_actions = Self::start_resource_senders(link, senders, now);
1743
1744 let _ = link;
1745 self.process_resource_actions(link_id, adv_actions, rng)
1746 }
1747
1748 pub fn send_deferred_response(
1750 &mut self,
1751 link_id: &LinkId,
1752 request_id: &[u8; 16],
1753 response_data: &[u8],
1754 rng: &mut dyn Rng,
1755 ) -> Vec<LinkManagerAction> {
1756 let packet_actions = self.build_response_packet(link_id, request_id, response_data, rng);
1757 if !packet_actions.is_empty() {
1758 packet_actions
1759 } else {
1760 self.send_response_resource(link_id, request_id, response_data, None, true, rng)
1761 }
1762 }
1763
1764 pub fn send_management_response(
1767 &mut self,
1768 link_id: &LinkId,
1769 request_id: &[u8; 16],
1770 response_data: &[u8],
1771 rng: &mut dyn Rng,
1772 ) -> Vec<LinkManagerAction> {
1773 let mut actions = self.build_response_packet(link_id, request_id, response_data, rng);
1774 if actions.is_empty() {
1775 actions.extend(self.send_response_resource(
1776 link_id,
1777 request_id,
1778 response_data,
1779 None,
1780 true,
1781 rng,
1782 ));
1783 }
1784 actions
1785 }
1786
1787 pub fn send_request(
1795 &mut self,
1796 link_id: &LinkId,
1797 path: &str,
1798 data: &[u8],
1799 rng: &mut dyn Rng,
1800 ) -> Vec<LinkManagerAction> {
1801 self.send_request_with_max_response_size(link_id, path, data, None, rng)
1802 }
1803
1804 pub fn send_request_with_max_response_size(
1806 &mut self,
1807 link_id: &LinkId,
1808 path: &str,
1809 data: &[u8],
1810 max_response_size: Option<usize>,
1811 rng: &mut dyn Rng,
1812 ) -> Vec<LinkManagerAction> {
1813 use rns_core::msgpack::{self, Value};
1814
1815 let link = match self.links.get_mut(link_id) {
1816 Some(l) => l,
1817 None => return Vec::new(),
1818 };
1819
1820 if link.engine.state() != LinkState::Active {
1821 return Vec::new();
1822 }
1823
1824 let path_hash = compute_path_hash(path);
1825
1826 let data_value = msgpack::unpack_exact(data).unwrap_or_else(|_| Value::Bin(data.to_vec()));
1828
1829 let request_array = Value::Array(vec![
1831 Value::Float(time::now()),
1832 Value::Bin(path_hash.to_vec()),
1833 data_value,
1834 ]);
1835 let plaintext = msgpack::pack(&request_array);
1836
1837 if plaintext.len() > link.engine.mdu() {
1838 let request_id = rns_core::hash::truncated_hash(&plaintext);
1839 let now = time::now();
1840 let senders = match Self::build_resource_senders(
1841 link,
1842 &plaintext,
1843 None,
1844 true,
1845 false,
1846 Some(request_id.to_vec()),
1847 rng,
1848 now,
1849 ) {
1850 Ok(senders) => senders,
1851 Err(e) => {
1852 log::debug!("Failed to create request ResourceSender: {}", e);
1853 return Vec::new();
1854 }
1855 };
1856 link.pending_requests.insert(
1859 request_id,
1860 PendingRequest {
1861 deadline: None,
1862 max_response_size,
1863 },
1864 );
1865 let adv_actions = Self::start_resource_senders(link, senders, now);
1866 let _ = link;
1867 return self.process_resource_actions(link_id, adv_actions, rng);
1868 }
1869
1870 let encrypted = match link.engine.encrypt(&plaintext, rng) {
1871 Ok(e) => e,
1872 Err(_) => return Vec::new(),
1873 };
1874
1875 let flags = PacketFlags {
1876 header_type: constants::HEADER_1,
1877 context_flag: constants::FLAG_UNSET,
1878 transport_type: constants::TRANSPORT_BROADCAST,
1879 destination_type: constants::DESTINATION_LINK,
1880 packet_type: constants::PACKET_TYPE_DATA,
1881 };
1882
1883 let mut actions = Vec::new();
1884 let max_mtu = link.engine.mtu() as usize;
1885 if let Ok((raw, packet_hash)) = RawPacket::pack_raw_with_hash_with_max_mtu(
1886 flags,
1887 0,
1888 link_id,
1889 None,
1890 constants::CONTEXT_REQUEST,
1891 &encrypted,
1892 max_mtu,
1893 ) {
1894 let mut request_id = [0u8; 16];
1895 request_id.copy_from_slice(&packet_hash[..16]);
1896 let deadline = Self::request_response_deadline(link, time::now());
1897 link.pending_requests.insert(
1898 request_id,
1899 PendingRequest {
1900 deadline: Some(deadline),
1901 max_response_size,
1902 },
1903 );
1904 actions.push(LinkManagerAction::SendPacket {
1905 raw,
1906 dest_type: constants::DESTINATION_LINK,
1907 attached_interface: None,
1908 });
1909 }
1910 actions
1911 }
1912
1913 pub fn send_on_link(
1915 &self,
1916 link_id: &LinkId,
1917 plaintext: &[u8],
1918 context: u8,
1919 rng: &mut dyn Rng,
1920 ) -> Vec<LinkManagerAction> {
1921 let link = match self.links.get(link_id) {
1922 Some(l) => l,
1923 None => return Vec::new(),
1924 };
1925
1926 if link.engine.state() != LinkState::Active {
1927 return Vec::new();
1928 }
1929
1930 let encrypted = match link.engine.encrypt(plaintext, rng) {
1931 Ok(e) => e,
1932 Err(_) => return Vec::new(),
1933 };
1934
1935 let flags = PacketFlags {
1936 header_type: constants::HEADER_1,
1937 context_flag: constants::FLAG_UNSET,
1938 transport_type: constants::TRANSPORT_BROADCAST,
1939 destination_type: constants::DESTINATION_LINK,
1940 packet_type: constants::PACKET_TYPE_DATA,
1941 };
1942
1943 let mut actions = Vec::new();
1944 if let Ok((raw, _packet_hash)) =
1945 RawPacket::pack_raw_with_hash(flags, 0, link_id, None, context, &encrypted)
1946 {
1947 actions.push(LinkManagerAction::SendPacket {
1948 raw,
1949 dest_type: constants::DESTINATION_LINK,
1950 attached_interface: None,
1951 });
1952 }
1953 actions
1954 }
1955
1956 pub fn identify(
1958 &self,
1959 link_id: &LinkId,
1960 identity: &rns_crypto::identity::Identity,
1961 rng: &mut dyn Rng,
1962 ) -> Vec<LinkManagerAction> {
1963 let link = match self.links.get(link_id) {
1964 Some(l) => l,
1965 None => return Vec::new(),
1966 };
1967
1968 let encrypted = match link.engine.build_identify(identity, rng) {
1969 Ok(e) => e,
1970 Err(_) => return Vec::new(),
1971 };
1972
1973 let flags = PacketFlags {
1974 header_type: constants::HEADER_1,
1975 context_flag: constants::FLAG_UNSET,
1976 transport_type: constants::TRANSPORT_BROADCAST,
1977 destination_type: constants::DESTINATION_LINK,
1978 packet_type: constants::PACKET_TYPE_DATA,
1979 };
1980
1981 let mut actions = Vec::new();
1982 if let Ok((raw, _packet_hash)) = RawPacket::pack_raw_with_hash(
1983 flags,
1984 0,
1985 link_id,
1986 None,
1987 constants::CONTEXT_LINKIDENTIFY,
1988 &encrypted,
1989 ) {
1990 actions.push(LinkManagerAction::SendPacket {
1991 raw,
1992 dest_type: constants::DESTINATION_LINK,
1993 attached_interface: None,
1994 });
1995 }
1996 actions
1997 }
1998
1999 pub fn teardown_link(&mut self, link_id: &LinkId) -> Vec<LinkManagerAction> {
2001 let link = match self.links.get_mut(link_id) {
2002 Some(l) => l,
2003 None => return Vec::new(),
2004 };
2005
2006 let previous_state = link.engine.state();
2007 if previous_state == LinkState::Closed {
2008 return Vec::new();
2009 }
2010
2011 let encrypted_close = if previous_state == LinkState::Pending {
2012 None
2013 } else {
2014 let mut rng = OsRng;
2015 link.engine.encrypt(link_id, &mut rng).ok()
2016 };
2017 let teardown_actions = link.engine.teardown();
2018 if let Some(ref mut channel) = link.channel {
2019 channel.shutdown();
2020 }
2021
2022 let mut actions = self.process_link_actions(link_id, &teardown_actions);
2023
2024 if previous_state == LinkState::Pending {
2027 return actions;
2028 }
2029
2030 let flags = PacketFlags {
2032 header_type: constants::HEADER_1,
2033 context_flag: constants::FLAG_UNSET,
2034 transport_type: constants::TRANSPORT_BROADCAST,
2035 destination_type: constants::DESTINATION_LINK,
2036 packet_type: constants::PACKET_TYPE_DATA,
2037 };
2038 if let Some(encrypted_close) = encrypted_close {
2039 if let Ok((raw, _packet_hash)) = RawPacket::pack_raw_with_hash(
2040 flags,
2041 0,
2042 link_id,
2043 None,
2044 constants::CONTEXT_LINKCLOSE,
2045 &encrypted_close,
2046 ) {
2047 actions.push(LinkManagerAction::SendPacket {
2048 raw,
2049 dest_type: constants::DESTINATION_LINK,
2050 attached_interface: None,
2051 });
2052 }
2053 }
2054
2055 actions
2056 }
2057
2058 pub fn teardown_all_links(&mut self) -> Vec<LinkManagerAction> {
2060 let link_ids: Vec<LinkId> = self.links.keys().copied().collect();
2061 let mut actions = Vec::new();
2062 for link_id in link_ids {
2063 actions.extend(self.teardown_link(&link_id));
2064 }
2065 actions
2066 }
2067
2068 fn handle_response(
2070 &mut self,
2071 link_id: &LinkId,
2072 plaintext: &[u8],
2073 metadata: Option<Vec<u8>>,
2074 resource_request_id: Option<[u8; 16]>,
2075 ) -> Vec<LinkManagerAction> {
2076 use rns_core::msgpack;
2077
2078 let packet_response = msgpack::unpack_exact(plaintext).ok().and_then(|value| {
2080 let msgpack::Value::Array(arr) = value else {
2081 return None;
2082 };
2083 if arr.len() < 2 {
2084 return None;
2085 }
2086 let msgpack::Value::Bin(request_id_bytes) = &arr[0] else {
2087 return None;
2088 };
2089 if request_id_bytes.len() != 16 {
2090 return None;
2091 }
2092 let mut request_id = [0u8; 16];
2093 request_id.copy_from_slice(request_id_bytes);
2094 Some((request_id, msgpack::pack(&arr[1])))
2095 });
2096
2097 let (request_id, response_data, check_size) = match packet_response {
2098 Some((request_id, response_data)) => (request_id, response_data, true),
2099 None => {
2100 let Some(request_id) = resource_request_id else {
2101 return Vec::new();
2102 };
2103 (
2104 request_id,
2105 msgpack::pack(&msgpack::Value::Bin(plaintext.to_vec())),
2106 false,
2107 )
2108 }
2109 };
2110
2111 let Some(link) = self.links.get_mut(link_id) else {
2112 return Vec::new();
2113 };
2114 let Some(pending) = link.pending_requests.get(&request_id) else {
2115 return Vec::new();
2116 };
2117 if check_size {
2118 let response_size = response_data.len().saturating_sub(2) as u64;
2122 if let Some(maximum) = pending.max_response_size {
2123 if response_size > maximum as u64 {
2124 link.pending_requests.remove(&request_id);
2125 log::debug!(
2126 "rejected response with excessive size {} bytes on link {:02x?}",
2127 response_size,
2128 &link_id[..4]
2129 );
2130 return vec![LinkManagerAction::RequestFailed {
2131 link_id: *link_id,
2132 request_id,
2133 reason: RequestFailure::ResponseTooLarge {
2134 size: response_size,
2135 maximum,
2136 },
2137 }];
2138 }
2139 }
2140 }
2141 link.pending_requests.remove(&request_id);
2142
2143 vec![LinkManagerAction::ResponseReceived {
2144 link_id: *link_id,
2145 request_id,
2146 data: response_data,
2147 metadata,
2148 }]
2149 }
2150
2151 fn response_request_id(request_id: &Option<Vec<u8>>) -> Option<[u8; 16]> {
2152 let bytes = request_id.as_deref()?;
2153 if bytes.len() != 16 {
2154 return None;
2155 }
2156 let mut out = [0u8; 16];
2157 out.copy_from_slice(bytes);
2158 Some(out)
2159 }
2160
2161 fn request_response_deadline(link: &ManagedLink, now: f64) -> f64 {
2162 now + link.engine.rtt().unwrap_or(1.0) * constants::LINK_TRAFFIC_TIMEOUT_FACTOR
2163 + constants::RESOURCE_RESPONSE_MAX_GRACE_TIME * 1.125
2164 }
2165
2166 fn build_resource_senders(
2167 link: &ManagedLink,
2168 data: &[u8],
2169 metadata: Option<&[u8]>,
2170 auto_compress: bool,
2171 is_response: bool,
2172 request_id: Option<Vec<u8>>,
2173 rng: &mut dyn Rng,
2174 now: f64,
2175 ) -> Result<Vec<ResourceSender>, rns_core::resource::ResourceError> {
2176 let link_rtt = link.engine.rtt().unwrap_or(1.0);
2177 let resource_sdu = Self::resource_sdu_for_link(link);
2178 let metadata_overhead = metadata.map(|m| 3 + m.len()).unwrap_or(0);
2179 let logical_size = metadata_overhead + data.len();
2180
2181 if logical_size <= constants::RESOURCE_MAX_EFFICIENT_SIZE {
2182 let enc_rng = std::cell::RefCell::new(rns_crypto::OsRng);
2183 let encrypt_fn = |plaintext: &[u8]| -> Vec<u8> {
2184 link.engine
2185 .encrypt(plaintext, &mut *enc_rng.borrow_mut())
2186 .unwrap_or_else(|_| plaintext.to_vec())
2187 };
2188 return ResourceSender::new(
2189 data,
2190 metadata,
2191 resource_sdu,
2192 &encrypt_fn,
2193 &Bzip2Compressor,
2194 rng,
2195 now,
2196 auto_compress,
2197 is_response,
2198 request_id,
2199 1,
2200 1,
2201 None,
2202 link_rtt,
2203 6.0,
2204 )
2205 .map(|sender| vec![sender]);
2206 }
2207
2208 if metadata_overhead > constants::RESOURCE_MAX_EFFICIENT_SIZE {
2209 return Err(rns_core::resource::ResourceError::TooLarge);
2210 }
2211
2212 let first_payload_len = core::cmp::min(
2213 data.len(),
2214 constants::RESOURCE_MAX_EFFICIENT_SIZE - metadata_overhead,
2215 );
2216 let remaining = data.len().saturating_sub(first_payload_len);
2217 let total_segments = 1 + remaining.div_ceil(constants::RESOURCE_MAX_EFFICIENT_SIZE) as u64;
2218
2219 let enc_rng = std::cell::RefCell::new(rns_crypto::OsRng);
2220 let encrypt_fn = |plaintext: &[u8]| -> Vec<u8> {
2221 link.engine
2222 .encrypt(plaintext, &mut *enc_rng.borrow_mut())
2223 .unwrap_or_else(|_| plaintext.to_vec())
2224 };
2225
2226 let mut senders = Vec::new();
2227 let mut first = ResourceSender::new(
2228 &data[..first_payload_len],
2229 metadata,
2230 resource_sdu,
2231 &encrypt_fn,
2232 &Bzip2Compressor,
2233 rng,
2234 now,
2235 auto_compress,
2236 is_response,
2237 request_id.clone(),
2238 1,
2239 total_segments,
2240 None,
2241 link_rtt,
2242 6.0,
2243 )?;
2244 first.data_size = logical_size;
2245 let original_hash = first.original_hash;
2246 let has_metadata = metadata.is_some();
2247 senders.push(first);
2248
2249 let mut offset = first_payload_len;
2250 let mut segment_index = 2;
2251 while offset < data.len() {
2252 let end = core::cmp::min(offset + constants::RESOURCE_MAX_EFFICIENT_SIZE, data.len());
2253 let mut sender = ResourceSender::new(
2254 &data[offset..end],
2255 None,
2256 resource_sdu,
2257 &encrypt_fn,
2258 &Bzip2Compressor,
2259 rng,
2260 now,
2261 auto_compress,
2262 is_response,
2263 request_id.clone(),
2264 segment_index,
2265 total_segments,
2266 Some(original_hash),
2267 link_rtt,
2268 6.0,
2269 )?;
2270 sender.data_size = logical_size;
2271 sender.flags.has_metadata = has_metadata;
2272 senders.push(sender);
2273 segment_index += 1;
2274 offset = end;
2275 }
2276
2277 Ok(senders)
2278 }
2279
2280 fn read_stream_segment(
2281 stream: &mut OutgoingStreamTransfer,
2282 capacity: usize,
2283 ) -> Result<Vec<u8>, ResourceTransferError> {
2284 let length = stream.remaining.min(capacity as u64) as usize;
2285 let mut data = vec![0; length];
2286 stream
2287 .reader
2288 .read_exact(&mut data)
2289 .map_err(|error| ResourceTransferError::Source(error.to_string()))?;
2290 stream.remaining -= length as u64;
2291 if stream.remaining == 0 {
2292 let mut trailing = [0u8; 1];
2293 if stream
2294 .reader
2295 .read(&mut trailing)
2296 .map_err(|error| ResourceTransferError::Source(error.to_string()))?
2297 != 0
2298 {
2299 return Err(ResourceTransferError::Source(
2300 "resource source exceeds declared length".into(),
2301 ));
2302 }
2303 }
2304 Ok(data)
2305 }
2306
2307 #[allow(clippy::too_many_arguments)]
2308 fn build_stream_segment(
2309 link: &ManagedLink,
2310 stream: &mut OutgoingStreamTransfer,
2311 metadata: Option<&[u8]>,
2312 segment_index: u64,
2313 total_segments: u64,
2314 original_hash: Option<[u8; 32]>,
2315 rng: &mut dyn Rng,
2316 now: f64,
2317 ) -> Result<ResourceSender, ResourceTransferError> {
2318 let metadata_overhead = metadata.map(|value| 3 + value.len()).unwrap_or(0);
2319 if metadata_overhead > constants::RESOURCE_MAX_EFFICIENT_SIZE {
2320 return Err(ResourceTransferError::Protocol(
2321 "resource metadata exceeds maximum segment size".into(),
2322 ));
2323 }
2324 let data = Self::read_stream_segment(
2325 stream,
2326 constants::RESOURCE_MAX_EFFICIENT_SIZE - metadata_overhead,
2327 )?;
2328 let enc_rng = std::cell::RefCell::new(rns_crypto::OsRng);
2329 let encrypt_fn = |plaintext: &[u8]| -> Vec<u8> {
2330 link.engine
2331 .encrypt(plaintext, &mut *enc_rng.borrow_mut())
2332 .unwrap_or_else(|_| plaintext.to_vec())
2333 };
2334 let mut sender = ResourceSender::new(
2335 &data,
2336 metadata,
2337 Self::resource_sdu_for_link(link),
2338 &encrypt_fn,
2339 &Bzip2Compressor,
2340 rng,
2341 now,
2342 stream.auto_compress,
2343 false,
2344 None,
2345 segment_index,
2346 total_segments,
2347 original_hash,
2348 link.engine.rtt().unwrap_or(1.0),
2349 6.0,
2350 )
2351 .map_err(|error| ResourceTransferError::Protocol(error.to_string()))?;
2352 sender.data_size = (stream.declared_length + stream.metadata_overhead) as usize;
2353 sender.flags.has_metadata = stream.metadata_overhead != 0;
2354 Ok(sender)
2355 }
2356
2357 fn start_resource_senders(
2358 link: &mut ManagedLink,
2359 mut senders: Vec<ResourceSender>,
2360 now: f64,
2361 ) -> Vec<ResourceAction> {
2362 if senders.is_empty() {
2363 return Vec::new();
2364 }
2365
2366 let mut first = senders.remove(0);
2367 let adv_actions = first.advertise(now);
2368
2369 if first.total_segments > 1 {
2370 let original_hash = first.original_hash;
2371 let split = OutgoingSplitTransfer {
2372 total_segments: first.total_segments,
2373 completed_segments: 0,
2374 current_segment_index: first.segment_index,
2375 current_sent_parts: 0,
2376 current_total_parts: first.total_parts(),
2377 };
2378 link.outgoing_splits.insert(original_hash, split);
2379 }
2380
2381 link.outgoing_resources.push(first);
2382 link.outgoing_resources.extend(senders);
2383 adv_actions
2384 }
2385
2386 fn handle_resource_adv(
2388 &mut self,
2389 link_id: &LinkId,
2390 adv_plaintext: &[u8],
2391 rng: &mut dyn Rng,
2392 ) -> Vec<LinkManagerAction> {
2393 let has_request_handlers = !self.request_handlers.is_empty()
2394 || !self.deferred_request_handlers.is_empty()
2395 || !self.management_paths.is_empty();
2396 let link = match self.links.get_mut(link_id) {
2397 Some(l) => l,
2398 None => return Vec::new(),
2399 };
2400
2401 let link_rtt = link.engine.rtt().unwrap_or(1.0);
2402 let resource_sdu = Self::resource_sdu_for_link(link);
2403 let now = time::now();
2404
2405 let receiver = match ResourceReceiver::from_advertisement(
2406 adv_plaintext,
2407 resource_sdu,
2408 link_rtt,
2409 now,
2410 None,
2411 None,
2412 ) {
2413 Ok(r) => r,
2414 Err(e) => {
2415 log::debug!("Resource ADV rejected: {}", e);
2416 let _ = link;
2417 return self.teardown_link(link_id);
2418 }
2419 };
2420
2421 let strategy = link.resource_strategy;
2422 let resource_hash = receiver.resource_hash.clone();
2423 let transfer_size = receiver.transfer_size;
2424 let has_metadata = receiver.has_metadata;
2425 let is_request = receiver.flags.is_request;
2426 let is_response = receiver.flags.is_response;
2427 let is_split = receiver.flags.split;
2428 let segment_index = receiver.segment_index;
2429 let total_segments = receiver.total_segments;
2430 let original_hash = match Self::resource_hash_key(&receiver.original_hash) {
2431 Some(key) => key,
2432 None => return Vec::new(),
2433 };
2434
2435 if !is_request && !is_response {
2436 let maximum = match &link.resource_receive_mode {
2437 ResourceReceiveMode::Memory { max_bytes } => Some(*max_bytes),
2438 ResourceReceiveMode::TemporaryFile { max_bytes, .. } => *max_bytes,
2439 };
2440 if maximum.is_some_and(|maximum| receiver.data_size > maximum) {
2441 let reject_actions = {
2442 let mut receiver = receiver;
2443 receiver.reject()
2444 };
2445 let _ = link;
2446 return self.process_resource_actions(link_id, reject_actions, rng);
2447 }
2448 }
2449
2450 if is_split && segment_index > 1 {
2451 let should_accept = link
2452 .incoming_splits
2453 .get(&original_hash)
2454 .is_some_and(|split| {
2455 split.completed_segments + 1 == segment_index
2456 && split.total_segments == total_segments
2457 });
2458
2459 if !should_accept {
2460 let reject_actions = {
2461 let mut r = receiver;
2462 r.reject()
2463 };
2464 let _ = link;
2465 return self.process_resource_actions(link_id, reject_actions, rng);
2466 }
2467
2468 let current_total_parts = receiver.total_parts;
2469 link.incoming_resources.push(receiver);
2470 let idx = link.incoming_resources.len() - 1;
2471 if let Some(split) = link.incoming_splits.get_mut(&original_hash) {
2472 split.current_segment_index = segment_index;
2473 split.current_received_parts = 0;
2474 split.current_total_parts = current_total_parts;
2475 }
2476 let resource_actions = link.incoming_resources[idx].accept(now);
2477 let _ = link;
2478 return self.process_resource_actions(link_id, resource_actions, rng);
2479 }
2480
2481 if is_request {
2482 if !has_request_handlers || Self::response_request_id(&receiver.request_id).is_none() {
2485 return Vec::new();
2486 }
2487 if link
2488 .max_request_size
2489 .is_some_and(|limit| receiver.data_size > limit as u64)
2490 {
2491 log::debug!(
2492 "ignored request with excessive size {} bytes on link {:02x?}",
2493 receiver.data_size,
2494 &link_id[..4]
2495 );
2496 let reject_actions = {
2497 let mut receiver = receiver;
2498 receiver.reject()
2499 };
2500 let _ = link;
2501 return self.process_resource_actions(link_id, reject_actions, rng);
2502 }
2503 if is_split {
2504 link.incoming_splits.insert(
2505 original_hash,
2506 IncomingSplitTransfer {
2507 total_segments,
2508 completed_segments: 0,
2509 current_segment_index: segment_index,
2510 current_received_parts: 0,
2511 current_total_parts: receiver.total_parts,
2512 storage: IncomingSplitStorage::Memory(Vec::new()),
2513 metadata: None,
2514 is_request: true,
2515 is_response: false,
2516 },
2517 );
2518 }
2519 link.incoming_resources.push(receiver);
2520 let idx = link.incoming_resources.len() - 1;
2521 let resource_actions = link.incoming_resources[idx].accept(now);
2522 let _ = link;
2523 return self.process_resource_actions(link_id, resource_actions, rng);
2524 }
2525
2526 if is_response {
2527 let Some(request_id) = Self::response_request_id(&receiver.request_id) else {
2530 return Vec::new();
2531 };
2532 let Some(pending) = link.pending_requests.get(&request_id) else {
2533 return Vec::new();
2534 };
2535 if let Some(maximum) = pending.max_response_size {
2536 if receiver.data_size > maximum as u64 {
2537 let response_size = receiver.data_size;
2538 link.pending_requests.remove(&request_id);
2539 log::debug!(
2540 "rejected response with excessive size {} bytes on link {:02x?}",
2541 response_size,
2542 &link_id[..4]
2543 );
2544 let reject_actions = {
2545 let mut receiver = receiver;
2546 receiver.reject()
2547 };
2548 let _ = link;
2549 let mut actions = self.process_resource_actions(link_id, reject_actions, rng);
2550 actions.push(LinkManagerAction::RequestFailed {
2551 link_id: *link_id,
2552 request_id,
2553 reason: RequestFailure::ResponseTooLarge {
2554 size: response_size,
2555 maximum,
2556 },
2557 });
2558 return actions;
2559 }
2560 }
2561 if is_split {
2562 link.incoming_splits.insert(
2563 original_hash,
2564 IncomingSplitTransfer {
2565 total_segments,
2566 completed_segments: 0,
2567 current_segment_index: segment_index,
2568 current_received_parts: 0,
2569 current_total_parts: receiver.total_parts,
2570 storage: IncomingSplitStorage::Memory(Vec::new()),
2571 metadata: None,
2572 is_request: false,
2573 is_response,
2574 },
2575 );
2576 }
2577 link.incoming_resources.push(receiver);
2578 let idx = link.incoming_resources.len() - 1;
2579 let resource_actions = link.incoming_resources[idx].accept(now);
2580 let _ = link;
2581 return self.process_resource_actions(link_id, resource_actions, rng);
2582 }
2583
2584 match strategy {
2585 ResourceStrategy::AcceptNone => {
2586 let reject_actions = {
2588 let mut r = receiver;
2589 r.reject()
2590 };
2591 self.process_resource_actions(link_id, reject_actions, rng)
2592 }
2593 ResourceStrategy::AcceptAll => {
2594 if is_split {
2595 let storage =
2596 match Self::incoming_storage(&link.resource_receive_mode, &original_hash) {
2597 Ok(storage) => storage,
2598 Err(error) => {
2599 let reject_actions = {
2600 let mut receiver = receiver;
2601 receiver.reject()
2602 };
2603 let _ = link;
2604 let mut actions =
2605 self.process_resource_actions(link_id, reject_actions, rng);
2606 actions.push(LinkManagerAction::ResourceFailed {
2607 link_id: *link_id,
2608 error: format!("resource storage failed: {error}"),
2609 });
2610 return actions;
2611 }
2612 };
2613 link.incoming_splits.insert(
2614 original_hash,
2615 IncomingSplitTransfer {
2616 total_segments,
2617 completed_segments: 0,
2618 current_segment_index: segment_index,
2619 current_received_parts: 0,
2620 current_total_parts: receiver.total_parts,
2621 storage,
2622 metadata: None,
2623 is_request: false,
2624 is_response,
2625 },
2626 );
2627 }
2628 link.incoming_resources.push(receiver);
2629 let idx = link.incoming_resources.len() - 1;
2630 let resource_actions = link.incoming_resources[idx].accept(now);
2631 let _ = link;
2632 self.process_resource_actions(link_id, resource_actions, rng)
2633 }
2634 ResourceStrategy::AcceptApp => {
2635 link.incoming_resources.push(receiver);
2636 vec![LinkManagerAction::ResourceAcceptQuery {
2638 link_id: *link_id,
2639 resource_hash,
2640 transfer_size,
2641 has_metadata,
2642 }]
2643 }
2644 }
2645 }
2646
2647 pub fn accept_resource(
2649 &mut self,
2650 link_id: &LinkId,
2651 resource_hash: &[u8],
2652 accept: bool,
2653 rng: &mut dyn Rng,
2654 ) -> Vec<LinkManagerAction> {
2655 let link = match self.links.get_mut(link_id) {
2656 Some(l) => l,
2657 None => return Vec::new(),
2658 };
2659
2660 let now = time::now();
2661 let idx = link
2662 .incoming_resources
2663 .iter()
2664 .position(|r| r.resource_hash == resource_hash);
2665 let idx = match idx {
2666 Some(i) => i,
2667 None => return Vec::new(),
2668 };
2669
2670 if accept && link.incoming_resources[idx].flags.split {
2671 if let Some(original_hash) =
2672 Self::resource_hash_key(&link.incoming_resources[idx].original_hash)
2673 {
2674 if !link.incoming_splits.contains_key(&original_hash) {
2675 let storage =
2676 match Self::incoming_storage(&link.resource_receive_mode, &original_hash) {
2677 Ok(storage) => storage,
2678 Err(error) => {
2679 let resource_actions = link.incoming_resources[idx].reject();
2680 let _ = link;
2681 let mut actions =
2682 self.process_resource_actions(link_id, resource_actions, rng);
2683 actions.push(LinkManagerAction::ResourceFailed {
2684 link_id: *link_id,
2685 error: format!("resource storage failed: {error}"),
2686 });
2687 return actions;
2688 }
2689 };
2690 link.incoming_splits.insert(
2691 original_hash,
2692 IncomingSplitTransfer {
2693 total_segments: link.incoming_resources[idx].total_segments,
2694 completed_segments: 0,
2695 current_segment_index: link.incoming_resources[idx].segment_index,
2696 current_received_parts: 0,
2697 current_total_parts: link.incoming_resources[idx].total_parts,
2698 storage,
2699 metadata: None,
2700 is_request: link.incoming_resources[idx].flags.is_request,
2701 is_response: link.incoming_resources[idx].flags.is_response,
2702 },
2703 );
2704 }
2705 }
2706 }
2707
2708 let resource_actions = if accept {
2709 link.incoming_resources[idx].accept(now)
2710 } else {
2711 link.incoming_resources[idx].reject()
2712 };
2713
2714 let _ = link;
2715 self.process_resource_actions(link_id, resource_actions, rng)
2716 }
2717
2718 fn handle_resource_req(
2720 &mut self,
2721 link_id: &LinkId,
2722 plaintext: &[u8],
2723 rng: &mut dyn Rng,
2724 ) -> Vec<LinkManagerAction> {
2725 let link = match self.links.get_mut(link_id) {
2726 Some(l) => l,
2727 None => return Vec::new(),
2728 };
2729
2730 let now = time::now();
2731 let mut all_actions = Vec::new();
2732 let mut progress_update = None;
2733 let mut progress_stream_hash = None;
2734 for sender in &mut link.outgoing_resources {
2735 if sender.flags.split && sender.status == rns_core::resource::ResourceStatus::Queued {
2736 continue;
2737 }
2738 let before_sent = sender.sent_parts;
2739 let resource_actions = sender.handle_request(plaintext, now);
2740 if !resource_actions.is_empty() {
2741 if sender.sent_parts != before_sent {
2742 progress_stream_hash = Some(sender.original_hash);
2743 if sender.flags.split {
2744 if let Some(split) = link.outgoing_splits.get_mut(&sender.original_hash) {
2745 split.current_segment_index = sender.segment_index;
2746 split.current_sent_parts = sender.sent_parts;
2747 split.current_total_parts = sender.total_parts();
2748 progress_update =
2749 Some(Self::outgoing_split_progress(split, sender.sdu));
2750 }
2751 } else {
2752 progress_update = Some((sender.sent_parts, sender.total_parts()));
2753 }
2754 }
2755 all_actions.extend(resource_actions);
2756 break;
2757 }
2758 }
2759
2760 let _ = link;
2761 let mut out = self.process_resource_actions(link_id, all_actions, rng);
2762 if let Some((received, total)) = progress_update {
2763 if let Some((stream, transferred)) = progress_stream_hash.and_then(|hash| {
2764 self.links.get(link_id).and_then(|link| {
2765 link.outgoing_streams.get(&hash).map(|stream| {
2766 let transferred = if total == 0 {
2767 0
2768 } else {
2769 stream.declared_length.saturating_mul(received as u64) / total as u64
2770 };
2771 (stream, transferred.min(stream.declared_length))
2772 })
2773 })
2774 }) {
2775 out.push(LinkManagerAction::ResourceStreamProgress {
2776 link_id: *link_id,
2777 transfer_id: stream.transfer_id,
2778 transferred,
2779 total: stream.declared_length,
2780 });
2781 } else {
2782 out.push(LinkManagerAction::ResourceProgress {
2783 link_id: *link_id,
2784 received,
2785 total,
2786 });
2787 }
2788 }
2789 out
2790 }
2791
2792 fn handle_resource_hmu(
2794 &mut self,
2795 link_id: &LinkId,
2796 plaintext: &[u8],
2797 rng: &mut dyn Rng,
2798 ) -> Vec<LinkManagerAction> {
2799 let link = match self.links.get_mut(link_id) {
2800 Some(l) => l,
2801 None => return Vec::new(),
2802 };
2803
2804 let now = time::now();
2805 let mut all_actions = Vec::new();
2806 for receiver in &mut link.incoming_resources {
2807 let resource_actions = receiver.handle_hashmap_update(plaintext, now);
2808 if !resource_actions.is_empty() {
2809 all_actions.extend(resource_actions);
2810 break;
2811 }
2812 }
2813
2814 let _ = link;
2815 self.process_resource_actions(link_id, all_actions, rng)
2816 }
2817
2818 fn handle_resource_part(
2820 &mut self,
2821 link_id: &LinkId,
2822 raw_data: &[u8],
2823 rng: &mut dyn Rng,
2824 ) -> Vec<LinkManagerAction> {
2825 let link = match self.links.get_mut(link_id) {
2826 Some(l) => l,
2827 None => return Vec::new(),
2828 };
2829
2830 let now = time::now();
2831 let resource_sdu = Self::resource_sdu_for_link(link);
2832 let mut all_actions = Vec::new();
2833 let mut assemble_idx = None;
2834 let mut assembled_is_request = false;
2835 let mut assembled_is_response = false;
2836 let mut request_request_id = None;
2837 let mut response_request_id = None;
2838 let mut completed_file = None;
2839 let mut storage_failure = None;
2840
2841 for (idx, receiver) in link.incoming_resources.iter_mut().enumerate() {
2842 if receiver.status >= rns_core::resource::ResourceStatus::Complete {
2843 continue;
2844 }
2845 let resource_actions = receiver.receive_part(raw_data, now);
2846 if !resource_actions.is_empty() {
2847 if receiver.received_count == receiver.total_parts {
2848 assemble_idx = Some(idx);
2849 }
2850 if receiver.flags.split {
2851 if let Some(key) = Self::resource_hash_key(&receiver.original_hash) {
2852 if let Some(split) = link.incoming_splits.get_mut(&key) {
2853 split.current_segment_index = receiver.segment_index;
2854 split.current_received_parts = receiver.received_count;
2855 split.current_total_parts = receiver.total_parts;
2856 let (received, total) =
2857 Self::incoming_split_progress(split, resource_sdu);
2858 for action in resource_actions {
2859 match action {
2860 ResourceAction::ProgressUpdate { .. } => {
2861 all_actions.push(ResourceAction::ProgressUpdate {
2862 received,
2863 total,
2864 });
2865 }
2866 other => all_actions.push(other),
2867 }
2868 }
2869 } else {
2870 all_actions.extend(resource_actions);
2871 }
2872 } else {
2873 all_actions.extend(resource_actions);
2874 }
2875 } else {
2876 all_actions.extend(resource_actions);
2877 }
2878 break;
2879 }
2880 }
2881
2882 if let Some(idx) = assemble_idx {
2883 let split_key = if link.incoming_resources[idx].flags.split {
2884 Self::resource_hash_key(&link.incoming_resources[idx].original_hash)
2885 } else {
2886 None
2887 };
2888 let split_segment_index = link.incoming_resources[idx].segment_index;
2889 let split_segment_total = link.incoming_resources[idx].total_segments;
2890 let split_segment_parts = link.incoming_resources[idx].total_parts;
2891 let split_is_request = link.incoming_resources[idx].flags.is_request;
2892 let split_is_response = link.incoming_resources[idx].flags.is_response;
2893 let resource_original_hash =
2894 Self::resource_hash_key(&link.incoming_resources[idx].original_hash)
2895 .unwrap_or([0; 32]);
2896 let resource_request_id =
2897 Self::response_request_id(&link.incoming_resources[idx].request_id);
2898 if split_is_request {
2899 request_request_id = resource_request_id;
2900 } else if split_is_response {
2901 response_request_id = resource_request_id;
2902 }
2903 let decrypt_fn = |ciphertext: &[u8]| -> Result<Vec<u8>, ()> {
2904 link.engine.decrypt(ciphertext).map_err(|_| ())
2905 };
2906 let mut assemble_actions =
2907 link.incoming_resources[idx].assemble(&decrypt_fn, &Bzip2Compressor);
2908 assembled_is_request = split_is_request;
2909 assembled_is_response = split_is_response;
2910
2911 if let Some(key) = split_key {
2912 let mut converted_actions = Vec::new();
2913 let mut segment_data = None;
2914 let mut segment_metadata = None;
2915 for action in assemble_actions {
2916 match action {
2917 ResourceAction::DataReceived { data, metadata } => {
2918 segment_data = Some(data);
2919 segment_metadata = metadata;
2920 }
2921 ResourceAction::Completed => {}
2922 other => converted_actions.push(other),
2923 }
2924 }
2925
2926 if let Some(data) = segment_data {
2927 if let Some(split) = link.incoming_splits.get_mut(&key) {
2928 let write_result = match &mut split.storage {
2929 IncomingSplitStorage::Memory(buffer) => {
2930 buffer.extend_from_slice(&data);
2931 Ok(())
2932 }
2933 IncomingSplitStorage::File { file, size, .. } => {
2934 file.write_all(&data).map(|()| *size += data.len() as u64)
2935 }
2936 };
2937 if let Err(error) = write_result {
2938 storage_failure = Some(error.to_string());
2939 }
2940 if segment_metadata.is_some() {
2941 split.metadata = segment_metadata;
2942 }
2943 split.completed_segments = split_segment_index;
2944 split.current_segment_index = split_segment_index;
2945 split.current_received_parts = split_segment_parts;
2946 split.current_total_parts = split_segment_parts;
2947 }
2948
2949 if storage_failure.is_none() && split_segment_index == split_segment_total {
2950 if let Some(split) = link.incoming_splits.remove(&key) {
2951 assembled_is_request = split.is_request;
2952 assembled_is_response = split.is_response;
2953 match split.storage {
2954 IncomingSplitStorage::Memory(data) => {
2955 converted_actions.push(ResourceAction::DataReceived {
2956 data,
2957 metadata: split.metadata,
2958 });
2959 }
2960 IncomingSplitStorage::File { file, path, size } => {
2961 match file.sync_all() {
2962 Ok(()) => {
2963 completed_file = Some(ReceivedResourceFile::new(
2964 path,
2965 key,
2966 size,
2967 split.metadata,
2968 ));
2969 }
2970 Err(error) => storage_failure = Some(error.to_string()),
2971 }
2972 }
2973 }
2974 converted_actions.push(ResourceAction::Completed);
2975 }
2976 }
2977 if storage_failure.is_some() {
2978 link.incoming_splits.remove(&key);
2979 converted_actions.clear();
2980 converted_actions.push(ResourceAction::SendCancelReceiver(Vec::new()));
2981 }
2982 }
2983
2984 assemble_actions = converted_actions;
2985 } else if !split_is_request
2986 && !split_is_response
2987 && matches!(
2988 link.resource_receive_mode,
2989 ResourceReceiveMode::TemporaryFile { .. }
2990 )
2991 {
2992 let directory = match &link.resource_receive_mode {
2993 ResourceReceiveMode::TemporaryFile { directory, .. } => directory.clone(),
2994 ResourceReceiveMode::Memory { .. } => unreachable!(),
2995 };
2996 let mut converted_actions = Vec::new();
2997 for action in assemble_actions {
2998 match action {
2999 ResourceAction::DataReceived { data, metadata } => {
3000 match crate::resource::create_receive_file(
3001 &directory,
3002 &resource_original_hash,
3003 )
3004 .and_then(|(mut file, path)| {
3005 file.write_all(&data)?;
3006 file.sync_all()?;
3007 Ok(ReceivedResourceFile::new(
3008 path,
3009 resource_original_hash,
3010 data.len() as u64,
3011 metadata,
3012 ))
3013 }) {
3014 Ok(resource) => completed_file = Some(resource),
3015 Err(error) => storage_failure = Some(error.to_string()),
3016 }
3017 }
3018 other => converted_actions.push(other),
3019 }
3020 }
3021 if storage_failure.is_some() {
3022 converted_actions.retain(|action| {
3023 !matches!(
3024 action,
3025 ResourceAction::SendProof(_) | ResourceAction::Completed
3026 )
3027 });
3028 converted_actions.push(ResourceAction::SendCancelReceiver(Vec::new()));
3029 }
3030 assemble_actions = converted_actions;
3031 }
3032 all_actions.extend(assemble_actions);
3033 }
3034
3035 let _ = link;
3036 let mut out = self.process_resource_actions(link_id, all_actions, rng);
3037 if let Some(resource) = completed_file {
3038 out.push(LinkManagerAction::ResourceFileReceived {
3039 link_id: *link_id,
3040 resource,
3041 });
3042 }
3043 if let Some(error) = storage_failure {
3044 out.push(LinkManagerAction::ResourceFailed {
3045 link_id: *link_id,
3046 error: format!("resource storage failed: {error}"),
3047 });
3048 }
3049
3050 if assembled_is_request {
3051 let mut converted = Vec::new();
3052 for action in out {
3053 match action {
3054 LinkManagerAction::ResourceReceived { data, .. } => {
3055 if let Some(request_id) = request_request_id {
3056 converted.extend(self.handle_request(link_id, &data, request_id, rng));
3057 }
3058 }
3059 LinkManagerAction::ResourceAcceptQuery { .. } => {
3060 }
3062 other => converted.push(other),
3063 }
3064 }
3065 out = converted;
3066 } else if assembled_is_response {
3067 let mut converted = Vec::new();
3068 for action in out {
3069 match action {
3070 LinkManagerAction::ResourceReceived { data, metadata, .. } => {
3071 converted.extend(self.handle_response(
3072 link_id,
3073 &data,
3074 metadata,
3075 response_request_id,
3076 ));
3077 }
3078 LinkManagerAction::ResourceAcceptQuery { .. } => {
3079 }
3081 other => converted.push(other),
3082 }
3083 }
3084 out = converted;
3085 }
3086
3087 out
3088 }
3089
3090 fn handle_resource_prf(
3092 &mut self,
3093 link_id: &LinkId,
3094 plaintext: &[u8],
3095 rng: &mut dyn Rng,
3096 ) -> Vec<LinkManagerAction> {
3097 let link = match self.links.get_mut(link_id) {
3098 Some(l) => l,
3099 None => return Vec::new(),
3100 };
3101
3102 let now = time::now();
3103 let mut result_actions = Vec::new();
3104 let mut completed_sender = None;
3105 let mut completed_request_id = None;
3106 let mut failed_split = None;
3107 let mut failed_request_id = None;
3108 let proof_hash = plaintext.get(..32);
3109 for sender in &mut link.outgoing_resources {
3110 if proof_hash.is_some_and(|hash| hash != sender.resource_hash.as_slice()) {
3111 continue;
3112 }
3113 let resource_actions = sender.handle_proof(plaintext, now);
3114 if !resource_actions.is_empty() {
3115 if resource_actions
3116 .iter()
3117 .any(|action| matches!(action, ResourceAction::Completed))
3118 {
3119 completed_sender = Some((
3120 sender.original_hash,
3121 sender.segment_index,
3122 sender.total_segments,
3123 sender.total_parts(),
3124 ));
3125 if sender.flags.is_request && sender.segment_index == sender.total_segments {
3126 completed_request_id = Self::response_request_id(&sender.request_id);
3127 }
3128 }
3129 if resource_actions
3130 .iter()
3131 .any(|action| matches!(action, ResourceAction::Failed(_)))
3132 {
3133 failed_split = Some(sender.original_hash);
3134 }
3135 if resource_actions
3136 .iter()
3137 .any(|action| matches!(action, ResourceAction::Failed(_)))
3138 && sender.flags.is_request
3139 {
3140 failed_request_id = Self::response_request_id(&sender.request_id);
3141 }
3142 result_actions.extend(resource_actions);
3143 break;
3144 }
3145 }
3146
3147 let mut actions = Vec::new();
3149 let mut advertise_next = None;
3150 let mut stream_failure = None;
3151 for ra in result_actions {
3152 match ra {
3153 ResourceAction::Completed => {
3154 if let Some((original_hash, segment_index, total_segments, total_parts)) =
3155 completed_sender
3156 {
3157 if total_segments > 1 && segment_index < total_segments {
3158 if let Some(split) = link.outgoing_splits.get_mut(&original_hash) {
3159 split.completed_segments = segment_index;
3160 split.current_segment_index = segment_index;
3161 split.current_sent_parts = total_parts;
3162 split.current_total_parts = total_parts;
3163 }
3164 if let Some(next) = link.outgoing_resources.iter_mut().find(|s| {
3165 s.flags.split
3166 && s.original_hash == original_hash
3167 && s.segment_index == segment_index + 1
3168 }) {
3169 if let Some(split) = link.outgoing_splits.get_mut(&original_hash) {
3170 split.current_segment_index = next.segment_index;
3171 split.current_sent_parts = 0;
3172 split.current_total_parts = next.total_parts();
3173 }
3174 advertise_next = Some(next.advertise(now));
3175 } else if let Some(mut stream) =
3176 link.outgoing_streams.remove(&original_hash)
3177 {
3178 match Self::build_stream_segment(
3179 link,
3180 &mut stream,
3181 None,
3182 segment_index + 1,
3183 total_segments,
3184 Some(original_hash),
3185 rng,
3186 now,
3187 ) {
3188 Ok(mut next) => {
3189 if let Some(split) =
3190 link.outgoing_splits.get_mut(&original_hash)
3191 {
3192 split.current_segment_index = next.segment_index;
3193 split.current_sent_parts = 0;
3194 split.current_total_parts = next.total_parts();
3195 }
3196 advertise_next = Some(next.advertise(now));
3197 link.outgoing_resources.push(next);
3198 link.outgoing_streams.insert(original_hash, stream);
3199 }
3200 Err(error) => {
3201 stream_failure = Some((stream.transfer_id, error));
3202 link.outgoing_splits.remove(&original_hash);
3203 }
3204 }
3205 }
3206 } else {
3207 link.outgoing_splits.remove(&original_hash);
3208 if let Some(stream) = link.outgoing_streams.remove(&original_hash) {
3209 actions.push(LinkManagerAction::ResourceStreamCompleted {
3210 link_id: *link_id,
3211 transfer_id: stream.transfer_id,
3212 });
3213 } else {
3214 actions.push(LinkManagerAction::ResourceCompleted {
3215 link_id: *link_id,
3216 });
3217 }
3218 }
3219 } else {
3220 actions.push(LinkManagerAction::ResourceCompleted { link_id: *link_id });
3221 }
3222 }
3223 ResourceAction::Failed(e) => {
3224 if let Some(original_hash) = failed_split {
3225 link.outgoing_splits.remove(&original_hash);
3226 if let Some(stream) = link.outgoing_streams.remove(&original_hash) {
3227 actions.push(LinkManagerAction::ResourceStreamFailed {
3228 link_id: *link_id,
3229 transfer_id: stream.transfer_id,
3230 error: ResourceTransferError::Protocol(e.to_string()),
3231 });
3232 continue;
3233 }
3234 }
3235 actions.push(LinkManagerAction::ResourceFailed {
3236 link_id: *link_id,
3237 error: format!("{}", e),
3238 });
3239 }
3240 _ => {}
3241 }
3242 }
3243
3244 if let Some((transfer_id, error)) = stream_failure {
3245 actions.push(LinkManagerAction::ResourceStreamFailed {
3246 link_id: *link_id,
3247 transfer_id,
3248 error,
3249 });
3250 }
3251
3252 if let Some(request_id) = completed_request_id {
3253 let deadline = Self::request_response_deadline(link, now);
3254 if let Some(entry) = link.pending_requests.get_mut(&request_id) {
3255 entry.deadline = Some(deadline);
3256 }
3257 }
3258 if let Some(request_id) = failed_request_id {
3259 link.pending_requests.remove(&request_id);
3260 }
3261
3262 link.outgoing_resources
3264 .retain(|s| s.status < rns_core::resource::ResourceStatus::Complete);
3265
3266 let _ = link;
3267 if let Some(next_actions) = advertise_next {
3268 actions.extend(self.process_resource_actions(link_id, next_actions, rng));
3269 }
3270
3271 actions
3272 }
3273
3274 fn handle_resource_icl(&mut self, link_id: &LinkId) -> Vec<LinkManagerAction> {
3276 let link = match self.links.get_mut(link_id) {
3277 Some(l) => l,
3278 None => return Vec::new(),
3279 };
3280
3281 let mut actions = Vec::new();
3282 for receiver in &mut link.incoming_resources {
3283 let ra = receiver.handle_cancel();
3284 for a in ra {
3285 if let ResourceAction::Failed(ref e) = a {
3286 actions.push(LinkManagerAction::ResourceFailed {
3287 link_id: *link_id,
3288 error: format!("{}", e),
3289 });
3290 }
3291 }
3292 }
3293 link.incoming_resources
3294 .retain(|r| r.status < rns_core::resource::ResourceStatus::Complete);
3295 link.incoming_splits.clear();
3296 actions
3297 }
3298
3299 fn handle_resource_rcl(&mut self, link_id: &LinkId) -> Vec<LinkManagerAction> {
3301 let link = match self.links.get_mut(link_id) {
3302 Some(l) => l,
3303 None => return Vec::new(),
3304 };
3305
3306 let request_ids: Vec<[u8; 16]> = link
3307 .outgoing_resources
3308 .iter()
3309 .filter(|sender| sender.flags.is_request)
3310 .filter_map(|sender| Self::response_request_id(&sender.request_id))
3311 .collect();
3312 let mut actions = Vec::new();
3313 for sender in &mut link.outgoing_resources {
3314 let ra = sender.handle_reject();
3315 for a in ra {
3316 if let ResourceAction::Failed(ref e) = a {
3317 actions.push(LinkManagerAction::ResourceFailed {
3318 link_id: *link_id,
3319 error: format!("{}", e),
3320 });
3321 }
3322 }
3323 }
3324 link.outgoing_resources
3325 .retain(|s| s.status < rns_core::resource::ResourceStatus::Complete);
3326 link.outgoing_splits.clear();
3327 actions.extend(link.outgoing_streams.drain().map(|(_, stream)| {
3328 LinkManagerAction::ResourceStreamFailed {
3329 link_id: *link_id,
3330 transfer_id: stream.transfer_id,
3331 error: ResourceTransferError::Cancelled,
3332 }
3333 }));
3334 for request_id in request_ids {
3335 link.pending_requests.remove(&request_id);
3336 }
3337 actions
3338 }
3339
3340 fn resource_link_is_active(&self, link_id: &LinkId) -> bool {
3341 self.links
3342 .get(link_id)
3343 .is_some_and(|link| link.engine.state() == LinkState::Active)
3344 }
3345
3346 fn abort_resources_on_inactive_link(&mut self, link_id: &LinkId) -> Vec<LinkManagerAction> {
3347 let Some(link) = self.links.get_mut(link_id) else {
3348 return Vec::new();
3349 };
3350 for sender in &mut link.outgoing_resources {
3351 let _ = sender.cancel();
3352 }
3353 for receiver in &mut link.incoming_resources {
3354 let _ = receiver.cancel();
3355 }
3356 link.outgoing_resources.clear();
3357 link.incoming_resources.clear();
3358 link.outgoing_splits.clear();
3359 let actions = link
3360 .outgoing_streams
3361 .drain()
3362 .map(|(_, stream)| LinkManagerAction::ResourceStreamFailed {
3363 link_id: *link_id,
3364 transfer_id: stream.transfer_id,
3365 error: ResourceTransferError::Cancelled,
3366 })
3367 .collect();
3368 link.incoming_splits.clear();
3369 link.pending_requests.clear();
3370 actions
3371 }
3372
3373 fn process_resource_actions(
3375 &mut self,
3376 link_id: &LinkId,
3377 actions: Vec<ResourceAction>,
3378 rng: &mut dyn Rng,
3379 ) -> Vec<LinkManagerAction> {
3380 let mut result = Vec::new();
3381 for action in actions {
3382 let requires_active_link = matches!(
3383 &action,
3384 ResourceAction::SendAdvertisement(_)
3385 | ResourceAction::SendPart(_)
3386 | ResourceAction::SendRequest(_)
3387 | ResourceAction::SendHmu(_)
3388 | ResourceAction::SendProof(_)
3389 );
3390 if requires_active_link && !self.resource_link_is_active(link_id) {
3391 result.extend(self.abort_resources_on_inactive_link(link_id));
3392 continue;
3393 }
3394
3395 match action {
3396 ResourceAction::SendAdvertisement(data) => {
3397 let encrypted = self
3399 .links
3400 .get(link_id)
3401 .and_then(|link| link.engine.encrypt(&data, rng).ok());
3402 if let Some(encrypted) = encrypted {
3403 result.extend(self.build_link_packet(
3404 link_id,
3405 constants::CONTEXT_RESOURCE_ADV,
3406 &encrypted,
3407 ));
3408 }
3409 }
3410 ResourceAction::SendPart(data) => {
3411 result.extend(self.build_link_packet(
3413 link_id,
3414 constants::CONTEXT_RESOURCE,
3415 &data,
3416 ));
3417 }
3418 ResourceAction::SendRequest(data) => {
3419 let encrypted = self
3420 .links
3421 .get(link_id)
3422 .and_then(|link| link.engine.encrypt(&data, rng).ok());
3423 if let Some(encrypted) = encrypted {
3424 result.extend(self.build_link_packet(
3425 link_id,
3426 constants::CONTEXT_RESOURCE_REQ,
3427 &encrypted,
3428 ));
3429 }
3430 }
3431 ResourceAction::SendHmu(data) => {
3432 let encrypted = self
3433 .links
3434 .get(link_id)
3435 .and_then(|link| link.engine.encrypt(&data, rng).ok());
3436 if let Some(encrypted) = encrypted {
3437 result.extend(self.build_link_packet(
3438 link_id,
3439 constants::CONTEXT_RESOURCE_HMU,
3440 &encrypted,
3441 ));
3442 }
3443 }
3444 ResourceAction::SendProof(data) => {
3445 let flags = PacketFlags {
3446 header_type: constants::HEADER_1,
3447 context_flag: constants::FLAG_UNSET,
3448 transport_type: constants::TRANSPORT_BROADCAST,
3449 destination_type: constants::DESTINATION_LINK,
3450 packet_type: constants::PACKET_TYPE_PROOF,
3451 };
3452 if let Ok((raw, _packet_hash)) = RawPacket::pack_raw_with_hash(
3453 flags,
3454 0,
3455 link_id,
3456 None,
3457 constants::CONTEXT_RESOURCE_PRF,
3458 &data,
3459 ) {
3460 result.push(LinkManagerAction::SendPacket {
3461 raw,
3462 dest_type: constants::DESTINATION_LINK,
3463 attached_interface: None,
3464 });
3465 }
3466 }
3467 ResourceAction::SendCancelInitiator(data) => {
3468 let encrypted = self
3469 .links
3470 .get(link_id)
3471 .filter(|link| link.engine.state() == LinkState::Active)
3472 .and_then(|link| link.engine.encrypt(&data, rng).ok());
3473 if let Some(encrypted) = encrypted {
3474 result.extend(self.build_link_packet(
3475 link_id,
3476 constants::CONTEXT_RESOURCE_ICL,
3477 &encrypted,
3478 ));
3479 }
3480 }
3481 ResourceAction::SendCancelReceiver(data) => {
3482 let encrypted = self
3483 .links
3484 .get(link_id)
3485 .filter(|link| link.engine.state() == LinkState::Active)
3486 .and_then(|link| link.engine.encrypt(&data, rng).ok());
3487 if let Some(encrypted) = encrypted {
3488 result.extend(self.build_link_packet(
3489 link_id,
3490 constants::CONTEXT_RESOURCE_RCL,
3491 &encrypted,
3492 ));
3493 }
3494 }
3495 ResourceAction::DataReceived { data, metadata } => {
3496 result.push(LinkManagerAction::ResourceReceived {
3497 link_id: *link_id,
3498 data,
3499 metadata,
3500 });
3501 }
3502 ResourceAction::Completed => {
3503 result.push(LinkManagerAction::ResourceCompleted { link_id: *link_id });
3504 }
3505 ResourceAction::Failed(e) => {
3506 result.push(LinkManagerAction::ResourceFailed {
3507 link_id: *link_id,
3508 error: format!("{}", e),
3509 });
3510 }
3511 ResourceAction::TeardownLink => {
3512 let teardown_actions = match self.links.get_mut(link_id) {
3513 Some(link) => link.engine.handle_teardown(),
3514 None => Vec::new(),
3515 };
3516 result.extend(self.process_link_actions(link_id, &teardown_actions));
3517 }
3518 ResourceAction::ProgressUpdate { received, total } => {
3519 result.push(LinkManagerAction::ResourceProgress {
3520 link_id: *link_id,
3521 received,
3522 total,
3523 });
3524 }
3525 }
3526 }
3527 result
3528 }
3529
3530 fn build_link_packet(
3532 &self,
3533 link_id: &LinkId,
3534 context: u8,
3535 data: &[u8],
3536 ) -> Vec<LinkManagerAction> {
3537 let flags = PacketFlags {
3538 header_type: constants::HEADER_1,
3539 context_flag: constants::FLAG_UNSET,
3540 transport_type: constants::TRANSPORT_BROADCAST,
3541 destination_type: constants::DESTINATION_LINK,
3542 packet_type: constants::PACKET_TYPE_DATA,
3543 };
3544 let mut actions = Vec::new();
3545 let max_mtu = self
3546 .links
3547 .get(link_id)
3548 .map(|l| l.engine.mtu() as usize)
3549 .unwrap_or(constants::MTU);
3550 if let Ok((raw, _packet_hash)) = RawPacket::pack_raw_with_hash_with_max_mtu(
3551 flags, 0, link_id, None, context, data, max_mtu,
3552 ) {
3553 actions.push(LinkManagerAction::SendPacket {
3554 raw,
3555 dest_type: constants::DESTINATION_LINK,
3556 attached_interface: None,
3557 });
3558 }
3559 actions
3560 }
3561
3562 pub fn send_resource(
3564 &mut self,
3565 link_id: &LinkId,
3566 data: &[u8],
3567 metadata: Option<&[u8]>,
3568 rng: &mut dyn Rng,
3569 ) -> Vec<LinkManagerAction> {
3570 self.send_resource_with_auto_compress(link_id, data, metadata, true, rng)
3571 }
3572
3573 pub fn send_resource_with_auto_compress(
3575 &mut self,
3576 link_id: &LinkId,
3577 data: &[u8],
3578 metadata: Option<&[u8]>,
3579 auto_compress: bool,
3580 rng: &mut dyn Rng,
3581 ) -> Vec<LinkManagerAction> {
3582 let link = match self.links.get_mut(link_id) {
3583 Some(l) => l,
3584 None => return Vec::new(),
3585 };
3586
3587 if link.engine.state() != LinkState::Active {
3588 return Vec::new();
3589 }
3590
3591 let now = time::now();
3592
3593 let senders = match Self::build_resource_senders(
3594 link,
3595 data,
3596 metadata,
3597 auto_compress,
3598 false, None, rng,
3601 now,
3602 ) {
3603 Ok(s) => s,
3604 Err(e) => {
3605 log::debug!("Failed to create ResourceSender: {}", e);
3606 return Vec::new();
3607 }
3608 };
3609
3610 let adv_actions = Self::start_resource_senders(link, senders, now);
3611
3612 let _ = link;
3613 self.process_resource_actions(link_id, adv_actions, rng)
3614 }
3615
3616 #[allow(clippy::too_many_arguments)]
3618 pub fn send_resource_stream(
3619 &mut self,
3620 link_id: &LinkId,
3621 transfer_id: ResourceTransferId,
3622 reader: Box<dyn Read + Send>,
3623 declared_length: u64,
3624 metadata: Option<Vec<u8>>,
3625 auto_compress: bool,
3626 rng: &mut dyn Rng,
3627 ) -> Vec<LinkManagerAction> {
3628 let Some(link) = self.links.get_mut(link_id) else {
3629 return vec![LinkManagerAction::ResourceStreamFailed {
3630 link_id: *link_id,
3631 transfer_id,
3632 error: ResourceTransferError::Protocol("link does not exist".into()),
3633 }];
3634 };
3635 if link.engine.state() != LinkState::Active {
3636 return vec![LinkManagerAction::ResourceStreamFailed {
3637 link_id: *link_id,
3638 transfer_id,
3639 error: ResourceTransferError::Protocol("link is not active".into()),
3640 }];
3641 }
3642
3643 let metadata_overhead = metadata.as_ref().map(|value| 3 + value.len()).unwrap_or(0);
3644 let Some(logical_size) = declared_length.checked_add(metadata_overhead as u64) else {
3645 return vec![LinkManagerAction::ResourceStreamFailed {
3646 link_id: *link_id,
3647 transfer_id,
3648 error: ResourceTransferError::Protocol("resource length overflow".into()),
3649 }];
3650 };
3651 if usize::try_from(logical_size).is_err()
3652 || metadata_overhead > constants::RESOURCE_MAX_EFFICIENT_SIZE
3653 {
3654 return vec![LinkManagerAction::ResourceStreamFailed {
3655 link_id: *link_id,
3656 transfer_id,
3657 error: ResourceTransferError::Protocol(
3658 "resource length cannot be represented by this platform".into(),
3659 ),
3660 }];
3661 }
3662
3663 let first_capacity = constants::RESOURCE_MAX_EFFICIENT_SIZE - metadata_overhead;
3664 let first_length = declared_length.min(first_capacity as u64);
3665 let remaining_after_first = declared_length - first_length;
3666 let total_segments =
3667 1 + remaining_after_first.div_ceil(constants::RESOURCE_MAX_EFFICIENT_SIZE as u64);
3668 let now = time::now();
3669 let mut stream = OutgoingStreamTransfer {
3670 transfer_id,
3671 reader,
3672 remaining: declared_length,
3673 declared_length,
3674 metadata_overhead: metadata_overhead as u64,
3675 auto_compress,
3676 };
3677 let mut first = match Self::build_stream_segment(
3678 link,
3679 &mut stream,
3680 metadata.as_deref(),
3681 1,
3682 total_segments,
3683 None,
3684 rng,
3685 now,
3686 ) {
3687 Ok(sender) => sender,
3688 Err(error) => {
3689 return vec![LinkManagerAction::ResourceStreamFailed {
3690 link_id: *link_id,
3691 transfer_id,
3692 error,
3693 }]
3694 }
3695 };
3696 let original_hash = first.original_hash;
3697 let current_total_parts = first.total_parts();
3698 let adv_actions = first.advertise(now);
3699 if total_segments > 1 {
3700 link.outgoing_splits.insert(
3701 original_hash,
3702 OutgoingSplitTransfer {
3703 total_segments,
3704 completed_segments: 0,
3705 current_segment_index: 1,
3706 current_sent_parts: 0,
3707 current_total_parts,
3708 },
3709 );
3710 }
3711 link.outgoing_streams.insert(original_hash, stream);
3712 link.outgoing_resources.push(first);
3713 let _ = link;
3714 self.process_resource_actions(link_id, adv_actions, rng)
3715 }
3716
3717 pub fn set_resource_strategy(&mut self, link_id: &LinkId, strategy: ResourceStrategy) {
3719 if let Some(link) = self.links.get_mut(link_id) {
3720 link.resource_strategy = strategy;
3721 }
3722 }
3723
3724 pub fn set_resource_receive_mode(&mut self, link_id: &LinkId, mode: ResourceReceiveMode) {
3725 if let Some(link) = self.links.get_mut(link_id) {
3726 link.resource_receive_mode = mode;
3727 }
3728 }
3729
3730 pub fn flush_channel_tx(&mut self, link_id: &LinkId) {
3733 if let Some(link) = self.links.get_mut(link_id) {
3734 if let Some(ref mut channel) = link.channel {
3735 channel.flush_tx();
3736 }
3737 }
3738 }
3739
3740 pub fn send_channel_message(
3742 &mut self,
3743 link_id: &LinkId,
3744 msgtype: u16,
3745 payload: &[u8],
3746 rng: &mut dyn Rng,
3747 ) -> Result<Vec<LinkManagerAction>, String> {
3748 let link = match self.links.get_mut(link_id) {
3749 Some(l) => l,
3750 None => return Err("unknown link".to_string()),
3751 };
3752
3753 let channel = match link.channel {
3754 Some(ref mut ch) => ch,
3755 None => return Err("link has no active channel".to_string()),
3756 };
3757
3758 let link_mdu = link.engine.mdu();
3759 let now = time::now();
3760 let chan_actions = match channel.send(msgtype, payload, now, link_mdu) {
3761 Ok(a) => {
3762 link.channel_send_ok += 1;
3763 a
3764 }
3765 Err(e) => {
3766 log::debug!("Channel send failed: {:?}", e);
3767 match e {
3768 rns_core::channel::ChannelError::NotReady => link.channel_send_not_ready += 1,
3769 rns_core::channel::ChannelError::MessageTooBig => {
3770 link.channel_send_too_big += 1;
3771 }
3772 rns_core::channel::ChannelError::InvalidEnvelope => {
3773 link.channel_send_other_error += 1;
3774 }
3775 }
3776 return Err(e.to_string());
3777 }
3778 };
3779
3780 let _ = link;
3781 Ok(self.process_channel_actions(link_id, chan_actions, rng))
3782 }
3783
3784 pub fn tick(&mut self, rng: &mut dyn Rng) -> Vec<LinkManagerAction> {
3786 let now = time::now();
3787 let mut all_actions = Vec::new();
3788
3789 let link_ids: Vec<LinkId> = self.links.keys().copied().collect();
3791
3792 for link_id in &link_ids {
3793 let link = match self.links.get_mut(link_id) {
3794 Some(l) => l,
3795 None => continue,
3796 };
3797
3798 let tick_actions = link.engine.tick(now);
3800 all_actions.extend(self.process_link_actions(link_id, &tick_actions));
3801
3802 let link = match self.links.get_mut(link_id) {
3804 Some(l) => l,
3805 None => continue,
3806 };
3807 if link.engine.needs_keepalive(now) {
3808 let flags = PacketFlags {
3811 header_type: constants::HEADER_1,
3812 context_flag: constants::FLAG_UNSET,
3813 transport_type: constants::TRANSPORT_BROADCAST,
3814 destination_type: constants::DESTINATION_LINK,
3815 packet_type: constants::PACKET_TYPE_DATA,
3816 };
3817 if let Ok((raw, _packet_hash)) = RawPacket::pack_raw_with_hash(
3818 flags,
3819 0,
3820 link_id,
3821 None,
3822 constants::CONTEXT_KEEPALIVE,
3823 &[0xff],
3824 ) {
3825 all_actions.push(LinkManagerAction::SendPacket {
3826 raw,
3827 dest_type: constants::DESTINATION_LINK,
3828 attached_interface: None,
3829 });
3830 link.engine.record_outbound(now, true);
3831 }
3832 }
3833
3834 if let Some(channel) = link.channel.as_mut() {
3835 let chan_actions = channel.tick(now);
3836 let _ = channel;
3837 let _ = link;
3838 all_actions.extend(self.process_channel_actions(link_id, chan_actions, rng));
3839 }
3840 }
3841
3842 for link_id in &link_ids {
3844 let link = match self.links.get_mut(link_id) {
3845 Some(l) => l,
3846 None => continue,
3847 };
3848
3849 let mut sender_actions = Vec::new();
3851 for sender in &mut link.outgoing_resources {
3852 sender_actions.extend(sender.tick(now));
3853 }
3854
3855 let mut receiver_actions = Vec::new();
3857 for receiver in &mut link.incoming_resources {
3858 let decrypt_fn = |ciphertext: &[u8]| -> Result<Vec<u8>, ()> {
3859 link.engine.decrypt(ciphertext).map_err(|_| ())
3860 };
3861 receiver_actions.extend(receiver.tick(now, &decrypt_fn, &Bzip2Compressor));
3862 }
3863
3864 let failed_request_ids: Vec<[u8; 16]> = link
3865 .outgoing_resources
3866 .iter()
3867 .filter(|sender| {
3868 sender.flags.is_request
3869 && sender.status >= rns_core::resource::ResourceStatus::Failed
3870 })
3871 .filter_map(|sender| Self::response_request_id(&sender.request_id))
3872 .collect();
3873 for request_id in failed_request_ids {
3874 link.pending_requests.remove(&request_id);
3875 }
3876 link.pending_requests
3877 .retain(|_, request| request.deadline.is_none_or(|expires_at| now <= expires_at));
3878
3879 link.outgoing_resources
3881 .retain(|s| s.status < rns_core::resource::ResourceStatus::Complete);
3882 link.incoming_resources
3883 .retain(|r| r.status < rns_core::resource::ResourceStatus::Assembling);
3884 let active_split_hashes: Vec<[u8; 32]> = link
3885 .outgoing_resources
3886 .iter()
3887 .filter(|s| s.flags.split)
3888 .map(|s| s.original_hash)
3889 .collect();
3890 link.outgoing_splits.retain(|original_hash, split| {
3891 split.completed_segments < split.total_segments
3892 && active_split_hashes.contains(original_hash)
3893 });
3894
3895 let _ = link;
3896 all_actions.extend(self.process_resource_actions(link_id, sender_actions, rng));
3897 all_actions.extend(self.process_resource_actions(link_id, receiver_actions, rng));
3898 }
3899
3900 let closed: Vec<LinkId> = self
3902 .links
3903 .iter()
3904 .filter(|(_, l)| l.engine.state() == LinkState::Closed)
3905 .map(|(id, _)| *id)
3906 .collect();
3907 for id in closed {
3908 self.links.remove(&id);
3909 all_actions.push(LinkManagerAction::DeregisterLinkDest { link_id: id });
3910 }
3911
3912 all_actions
3913 }
3914
3915 pub fn is_link_destination(&self, dest_hash: &[u8; 16]) -> bool {
3917 self.links.contains_key(dest_hash) || self.link_destinations.contains_key(dest_hash)
3918 }
3919
3920 pub fn link_state(&self, link_id: &LinkId) -> Option<LinkState> {
3922 self.links.get(link_id).map(|l| l.engine.state())
3923 }
3924
3925 pub fn link_rtt(&self, link_id: &LinkId) -> Option<f64> {
3927 self.links.get(link_id).and_then(|l| l.engine.rtt())
3928 }
3929
3930 pub fn set_link_rtt(&mut self, link_id: &LinkId, rtt: f64) {
3932 if let Some(link) = self.links.get_mut(link_id) {
3933 link.engine.set_rtt(rtt);
3934 }
3935 }
3936
3937 pub fn record_link_inbound(&mut self, link_id: &LinkId) {
3939 if let Some(link) = self.links.get_mut(link_id) {
3940 link.engine.record_inbound(time::now());
3941 }
3942 }
3943
3944 pub fn set_link_mtu(&mut self, link_id: &LinkId, mtu: u32) {
3946 if let Some(link) = self.links.get_mut(link_id) {
3947 link.engine.set_mtu(mtu);
3948 }
3949 }
3950
3951 pub fn link_count(&self) -> usize {
3953 self.links.len()
3954 }
3955
3956 pub fn resource_transfer_count(&self) -> usize {
3958 self.links
3959 .values()
3960 .map(|managed| {
3961 managed
3962 .incoming_resources
3963 .iter()
3964 .filter(|resource| !resource.flags.split)
3965 .count()
3966 + managed.incoming_splits.len()
3967 + managed
3968 .outgoing_resources
3969 .iter()
3970 .filter(|resource| !resource.flags.split)
3971 .count()
3972 + managed.outgoing_splits.len()
3973 })
3974 .sum()
3975 }
3976
3977 pub fn cancel_all_resources(&mut self, rng: &mut dyn Rng) -> Vec<LinkManagerAction> {
3979 let link_ids: Vec<LinkId> = self.links.keys().copied().collect();
3980 let mut all_actions = Vec::new();
3981
3982 for link_id in &link_ids {
3983 let link = match self.links.get_mut(link_id) {
3984 Some(l) => l,
3985 None => continue,
3986 };
3987
3988 let mut sender_actions = Vec::new();
3989 for sender in &mut link.outgoing_resources {
3990 sender_actions.extend(sender.cancel());
3991 }
3992
3993 let mut receiver_actions = Vec::new();
3994 for receiver in &mut link.incoming_resources {
3995 receiver_actions.extend(receiver.cancel());
3996 }
3997
3998 link.outgoing_resources
3999 .retain(|s| s.status < rns_core::resource::ResourceStatus::Complete);
4000 link.incoming_resources
4001 .retain(|r| r.status < rns_core::resource::ResourceStatus::Assembling);
4002 link.outgoing_splits.clear();
4003 all_actions.extend(link.outgoing_streams.drain().map(|(_, stream)| {
4004 LinkManagerAction::ResourceStreamFailed {
4005 link_id: *link_id,
4006 transfer_id: stream.transfer_id,
4007 error: ResourceTransferError::Cancelled,
4008 }
4009 }));
4010 link.incoming_splits.clear();
4011
4012 let _ = link;
4013 all_actions.extend(self.process_resource_actions(link_id, sender_actions, rng));
4014 all_actions.extend(self.process_resource_actions(link_id, receiver_actions, rng));
4015 }
4016
4017 all_actions
4018 }
4019
4020 pub fn link_entries(&self) -> Vec<crate::event::LinkInfoEntry> {
4022 self.links
4023 .iter()
4024 .map(|(link_id, managed)| {
4025 let state = match managed.engine.state() {
4026 LinkState::Pending => "pending",
4027 LinkState::Handshake => "handshake",
4028 LinkState::Active => "active",
4029 LinkState::Stale => "stale",
4030 LinkState::Closed => "closed",
4031 };
4032 crate::event::LinkInfoEntry {
4033 link_id: *link_id,
4034 state: state.to_string(),
4035 is_initiator: managed.engine.is_initiator(),
4036 dest_hash: managed.dest_hash,
4037 remote_identity: managed.remote_identity.as_ref().map(|(h, _)| *h),
4038 rtt: managed.engine.rtt(),
4039 expected_hops: managed.engine.expected_hops(),
4040 tx_packets: managed.engine.tx_packets(),
4041 rx_packets: managed.engine.rx_packets(),
4042 tx_bytes: managed.engine.tx_bytes(),
4043 rx_bytes: managed.engine.rx_bytes(),
4044 channel_window: managed.channel.as_ref().map(|c| c.window()),
4045 channel_outstanding: managed.channel.as_ref().map(|c| c.outstanding()),
4046 pending_channel_packets: managed.pending_channel_packets.len(),
4047 channel_send_ok: managed.channel_send_ok,
4048 channel_send_not_ready: managed.channel_send_not_ready,
4049 channel_send_too_big: managed.channel_send_too_big,
4050 channel_send_other_error: managed.channel_send_other_error,
4051 channel_messages_received: managed.channel_messages_received,
4052 channel_proofs_sent: managed.channel_proofs_sent,
4053 channel_proofs_received: managed.channel_proofs_received,
4054 }
4055 })
4056 .collect()
4057 }
4058
4059 pub fn record_outbound_packet(&mut self, packet: &RawPacket) {
4063 if packet.flags.destination_type == constants::DESTINATION_LINK {
4064 if let Some(link) = self.links.get_mut(&packet.destination_hash) {
4065 link.engine.record_outbound_traffic(packet.data.len());
4066 }
4067 }
4068 }
4069
4070 pub fn resource_entries(&self) -> Vec<crate::event::ResourceInfoEntry> {
4072 let mut entries = Vec::new();
4073 for (link_id, managed) in &self.links {
4074 let resource_sdu = Self::resource_sdu_for_link(managed);
4075 for split in managed.incoming_splits.values() {
4076 let (received, total) = Self::incoming_split_progress(split, resource_sdu);
4077 entries.push(crate::event::ResourceInfoEntry {
4078 link_id: *link_id,
4079 direction: "incoming".to_string(),
4080 total_parts: total,
4081 transferred_parts: received,
4082 complete: received >= total && total > 0,
4083 });
4084 }
4085 for recv in &managed.incoming_resources {
4086 if recv.flags.split {
4087 continue;
4088 }
4089 let (received, total) = recv.progress();
4090 entries.push(crate::event::ResourceInfoEntry {
4091 link_id: *link_id,
4092 direction: "incoming".to_string(),
4093 total_parts: total,
4094 transferred_parts: received,
4095 complete: received >= total && total > 0,
4096 });
4097 }
4098 for split in managed.outgoing_splits.values() {
4099 let (sent, total) = Self::outgoing_split_progress(split, resource_sdu);
4100 entries.push(crate::event::ResourceInfoEntry {
4101 link_id: *link_id,
4102 direction: "outgoing".to_string(),
4103 total_parts: total,
4104 transferred_parts: sent,
4105 complete: sent >= total && total > 0,
4106 });
4107 }
4108 for send in &managed.outgoing_resources {
4109 if send.flags.split {
4110 continue;
4111 }
4112 let total = send.total_parts();
4113 let sent = send.sent_parts;
4114 entries.push(crate::event::ResourceInfoEntry {
4115 link_id: *link_id,
4116 direction: "outgoing".to_string(),
4117 total_parts: total,
4118 transferred_parts: sent,
4119 complete: sent >= total && total > 0,
4120 });
4121 }
4122 }
4123 entries
4124 }
4125
4126 fn process_link_actions(
4128 &self,
4129 link_id: &LinkId,
4130 actions: &[LinkAction],
4131 ) -> Vec<LinkManagerAction> {
4132 let mut result = Vec::new();
4133 for action in actions {
4134 match action {
4135 LinkAction::StateChanged {
4136 new_state, reason, ..
4137 } => match new_state {
4138 LinkState::Closed => {
4139 result.push(LinkManagerAction::LinkClosed {
4140 link_id: *link_id,
4141 reason: *reason,
4142 });
4143 }
4144 _ => {}
4145 },
4146 LinkAction::LinkEstablished {
4147 rtt, is_initiator, ..
4148 } => {
4149 let dest_hash = self
4150 .links
4151 .get(link_id)
4152 .map(|l| l.dest_hash)
4153 .unwrap_or([0u8; 16]);
4154 let hops = self
4155 .links
4156 .get(link_id)
4157 .map(|link| link.engine.expected_hops())
4158 .unwrap_or(rns_core::constants::PATHFINDER_M);
4159 let rebalanced_at = self
4160 .links
4161 .get(link_id)
4162 .and_then(|link| link.engine.rebalanced_at());
4163 result.push(LinkManagerAction::LinkEstablished {
4164 link_id: *link_id,
4165 dest_hash,
4166 hops,
4167 rebalanced_at,
4168 rtt: *rtt,
4169 is_initiator: *is_initiator,
4170 });
4171 }
4172 LinkAction::RemoteIdentified {
4173 identity_hash,
4174 public_key,
4175 ..
4176 } => {
4177 result.push(LinkManagerAction::RemoteIdentified {
4178 link_id: *link_id,
4179 identity_hash: *identity_hash,
4180 public_key: *public_key,
4181 });
4182 }
4183 LinkAction::DataReceived { .. } => {
4184 }
4186 }
4187 }
4188 result
4189 }
4190
4191 fn process_channel_actions(
4193 &mut self,
4194 link_id: &LinkId,
4195 actions: Vec<rns_core::channel::ChannelAction>,
4196 rng: &mut dyn Rng,
4197 ) -> Vec<LinkManagerAction> {
4198 let mut result = Vec::new();
4199 for action in actions {
4200 match action {
4201 rns_core::channel::ChannelAction::SendOnLink { raw, sequence } => {
4202 let encrypted = match self.links.get(link_id) {
4206 Some(link) => match link.engine.encrypt(&raw, rng) {
4207 Ok(encrypted) => encrypted,
4208 Err(_) => {
4209 if let Some(link_mut) = self.links.get_mut(link_id) {
4210 if let Some(channel) = link_mut.channel.as_mut() {
4211 channel.cancel_send(sequence);
4212 }
4213 }
4214 continue;
4215 }
4216 },
4217 None => continue,
4218 };
4219 let flags = PacketFlags {
4220 header_type: constants::HEADER_1,
4221 context_flag: constants::FLAG_UNSET,
4222 transport_type: constants::TRANSPORT_BROADCAST,
4223 destination_type: constants::DESTINATION_LINK,
4224 packet_type: constants::PACKET_TYPE_DATA,
4225 };
4226 match RawPacket::pack_raw_with_hash(
4227 flags,
4228 0,
4229 link_id,
4230 None,
4231 constants::CONTEXT_CHANNEL,
4232 &encrypted,
4233 ) {
4234 Ok((raw_bytes, packet_hash)) => {
4235 if let Some(link_mut) = self.links.get_mut(link_id) {
4236 link_mut
4237 .pending_channel_packets
4238 .insert(packet_hash, sequence);
4239 }
4240 result.push(LinkManagerAction::SendPacket {
4241 raw: raw_bytes,
4242 dest_type: constants::DESTINATION_LINK,
4243 attached_interface: None,
4244 });
4245 }
4246 Err(_) => {
4247 if let Some(link_mut) = self.links.get_mut(link_id) {
4248 if let Some(channel) = link_mut.channel.as_mut() {
4249 channel.cancel_send(sequence);
4250 }
4251 }
4252 }
4253 }
4254 }
4255 rns_core::channel::ChannelAction::MessageReceived {
4256 msgtype, payload, ..
4257 } => {
4258 result.push(LinkManagerAction::ChannelMessageReceived {
4259 link_id: *link_id,
4260 msgtype,
4261 payload,
4262 });
4263 }
4264 rns_core::channel::ChannelAction::TeardownLink => {
4265 result.push(LinkManagerAction::LinkClosed {
4266 link_id: *link_id,
4267 reason: Some(TeardownReason::Timeout),
4268 });
4269 }
4270 }
4271 }
4272 result
4273 }
4274}
4275
4276impl Default for LinkManager {
4277 fn default() -> Self {
4278 Self::new()
4279 }
4280}
4281
4282fn compute_path_hash(path: &str) -> [u8; 16] {
4285 let full = rns_core::hash::full_hash(path.as_bytes());
4286 let mut result = [0u8; 16];
4287 result.copy_from_slice(&full[..16]);
4288 result
4289}
4290
4291#[cfg(test)]
4292mod tests {
4293 use super::*;
4294 use rns_crypto::identity::Identity;
4295 use rns_crypto::{FixedRng, OsRng};
4296
4297 fn make_rng(seed: u8) -> FixedRng {
4298 FixedRng::new(&[seed; 128])
4299 }
4300
4301 fn make_dest_keys(rng: &mut dyn Rng) -> (Ed25519PrivateKey, [u8; 32]) {
4302 let sig_prv = Ed25519PrivateKey::generate(rng);
4303 let sig_pub_bytes = sig_prv.public_key().public_bytes();
4304 (sig_prv, sig_pub_bytes)
4305 }
4306
4307 #[test]
4308 fn test_register_link_destination() {
4309 let mut mgr = LinkManager::new();
4310 let mut rng = make_rng(0x01);
4311 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
4312 let dest_hash = [0xDD; 16];
4313
4314 mgr.register_link_destination(
4315 dest_hash,
4316 sig_prv,
4317 sig_pub_bytes,
4318 ResourceStrategy::AcceptNone,
4319 );
4320 assert!(mgr.is_link_destination(&dest_hash));
4321
4322 mgr.deregister_link_destination(&dest_hash);
4323 assert!(!mgr.is_link_destination(&dest_hash));
4324 }
4325
4326 #[test]
4327 fn test_create_link() {
4328 let mut mgr = LinkManager::new();
4329 let mut rng = OsRng;
4330 let dest_hash = [0xDD; 16];
4331
4332 let sig_pub_bytes = [0xAA; 32]; let (link_id, actions) = mgr.create_link(
4334 &dest_hash,
4335 &sig_pub_bytes,
4336 1,
4337 constants::MTU as u32,
4338 &mut rng,
4339 );
4340 assert_ne!(link_id, [0u8; 16]);
4341 assert_eq!(actions.len(), 2);
4343 assert!(matches!(
4344 actions[0],
4345 LinkManagerAction::RegisterLinkDest { .. }
4346 ));
4347 assert!(matches!(actions[1], LinkManagerAction::SendPacket { .. }));
4348
4349 assert_eq!(mgr.link_state(&link_id), Some(LinkState::Pending));
4351 }
4352
4353 #[test]
4354 fn test_full_handshake_via_manager() {
4355 let mut rng = OsRng;
4356 let dest_hash = [0xDD; 16];
4357
4358 let mut responder_mgr = LinkManager::new();
4360 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
4361 responder_mgr.register_link_destination(
4362 dest_hash,
4363 sig_prv,
4364 sig_pub_bytes,
4365 ResourceStrategy::AcceptNone,
4366 );
4367
4368 let mut initiator_mgr = LinkManager::new();
4370
4371 let (link_id, init_actions) = initiator_mgr.create_link(
4373 &dest_hash,
4374 &sig_pub_bytes,
4375 1,
4376 constants::MTU as u32,
4377 &mut rng,
4378 );
4379 assert_eq!(init_actions.len(), 2);
4380
4381 let linkrequest_raw = match &init_actions[1] {
4383 LinkManagerAction::SendPacket { raw, .. } => raw.clone(),
4384 _ => panic!("Expected SendPacket"),
4385 };
4386
4387 let lr_packet = RawPacket::unpack(&linkrequest_raw).unwrap();
4389
4390 let resp_actions = responder_mgr.handle_local_delivery(
4392 lr_packet.destination_hash,
4393 &linkrequest_raw,
4394 lr_packet.packet_hash,
4395 rns_core::transport::types::InterfaceId(0),
4396 &mut rng,
4397 );
4398 assert!(resp_actions.len() >= 2);
4400 assert!(matches!(
4401 resp_actions[0],
4402 LinkManagerAction::RegisterLinkDest { .. }
4403 ));
4404
4405 let lrproof_raw = match &resp_actions[1] {
4407 LinkManagerAction::SendPacket { raw, .. } => raw.clone(),
4408 _ => panic!("Expected SendPacket for LRPROOF"),
4409 };
4410
4411 let lrproof_packet = RawPacket::unpack(&lrproof_raw).unwrap();
4413 let init_actions2 = initiator_mgr.handle_local_delivery(
4414 lrproof_packet.destination_hash,
4415 &lrproof_raw,
4416 lrproof_packet.packet_hash,
4417 rns_core::transport::types::InterfaceId(0),
4418 &mut rng,
4419 );
4420
4421 let has_established = init_actions2
4423 .iter()
4424 .any(|a| matches!(a, LinkManagerAction::LinkEstablished { .. }));
4425 assert!(has_established, "Initiator should emit LinkEstablished");
4426
4427 let lrrtt_raw = init_actions2
4429 .iter()
4430 .find_map(|a| match a {
4431 LinkManagerAction::SendPacket { raw, .. } => Some(raw.clone()),
4432 _ => None,
4433 })
4434 .expect("Should have LRRTT SendPacket");
4435
4436 let lrrtt_packet = RawPacket::unpack(&lrrtt_raw).unwrap();
4438 let resp_link_id = lrrtt_packet.destination_hash;
4439 let resp_actions2 = responder_mgr.handle_local_delivery(
4440 resp_link_id,
4441 &lrrtt_raw,
4442 lrrtt_packet.packet_hash,
4443 rns_core::transport::types::InterfaceId(0),
4444 &mut rng,
4445 );
4446
4447 let has_established = resp_actions2
4448 .iter()
4449 .any(|a| matches!(a, LinkManagerAction::LinkEstablished { .. }));
4450 assert!(has_established, "Responder should emit LinkEstablished");
4451
4452 assert_eq!(initiator_mgr.link_state(&link_id), Some(LinkState::Active));
4454 assert_eq!(responder_mgr.link_state(&link_id), Some(LinkState::Active));
4455
4456 assert!(initiator_mgr.link_rtt(&link_id).is_some());
4458 assert!(responder_mgr.link_rtt(&link_id).is_some());
4459 }
4460
4461 #[test]
4462 fn test_encrypted_data_exchange() {
4463 let mut rng = OsRng;
4464 let dest_hash = [0xDD; 16];
4465 let mut resp_mgr = LinkManager::new();
4466 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
4467 resp_mgr.register_link_destination(
4468 dest_hash,
4469 sig_prv,
4470 sig_pub_bytes,
4471 ResourceStrategy::AcceptNone,
4472 );
4473 let mut init_mgr = LinkManager::new();
4474
4475 let (link_id, init_actions) = init_mgr.create_link(
4477 &dest_hash,
4478 &sig_pub_bytes,
4479 1,
4480 constants::MTU as u32,
4481 &mut rng,
4482 );
4483 let lr_raw = extract_send_packet(&init_actions);
4484 let lr_pkt = RawPacket::unpack(&lr_raw).unwrap();
4485 let resp_actions = resp_mgr.handle_local_delivery(
4486 lr_pkt.destination_hash,
4487 &lr_raw,
4488 lr_pkt.packet_hash,
4489 rns_core::transport::types::InterfaceId(0),
4490 &mut rng,
4491 );
4492 let lrproof_raw = extract_send_packet_at(&resp_actions, 1);
4493 let lrproof_pkt = RawPacket::unpack(&lrproof_raw).unwrap();
4494 let init_actions2 = init_mgr.handle_local_delivery(
4495 lrproof_pkt.destination_hash,
4496 &lrproof_raw,
4497 lrproof_pkt.packet_hash,
4498 rns_core::transport::types::InterfaceId(0),
4499 &mut rng,
4500 );
4501 let lrrtt_raw = extract_any_send_packet(&init_actions2);
4502 let lrrtt_pkt = RawPacket::unpack(&lrrtt_raw).unwrap();
4503 resp_mgr.handle_local_delivery(
4504 lrrtt_pkt.destination_hash,
4505 &lrrtt_raw,
4506 lrrtt_pkt.packet_hash,
4507 rns_core::transport::types::InterfaceId(0),
4508 &mut rng,
4509 );
4510
4511 let actions =
4513 init_mgr.send_on_link(&link_id, b"hello link!", constants::CONTEXT_NONE, &mut rng);
4514 assert_eq!(actions.len(), 1);
4515 assert!(matches!(actions[0], LinkManagerAction::SendPacket { .. }));
4516 }
4517
4518 #[test]
4519 fn test_request_response() {
4520 let mut rng = OsRng;
4521 let dest_hash = [0xDD; 16];
4522 let mut resp_mgr = LinkManager::new();
4523 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
4524 resp_mgr.register_link_destination(
4525 dest_hash,
4526 sig_prv,
4527 sig_pub_bytes,
4528 ResourceStrategy::AcceptNone,
4529 );
4530
4531 resp_mgr.register_request_handler("/status", None, |_link_id, _path, _data, _remote| {
4533 Some(b"OK".to_vec())
4534 });
4535
4536 let mut init_mgr = LinkManager::new();
4537
4538 let (link_id, init_actions) = init_mgr.create_link(
4540 &dest_hash,
4541 &sig_pub_bytes,
4542 1,
4543 constants::MTU as u32,
4544 &mut rng,
4545 );
4546 let lr_raw = extract_send_packet(&init_actions);
4547 let lr_pkt = RawPacket::unpack(&lr_raw).unwrap();
4548 let resp_actions = resp_mgr.handle_local_delivery(
4549 lr_pkt.destination_hash,
4550 &lr_raw,
4551 lr_pkt.packet_hash,
4552 rns_core::transport::types::InterfaceId(0),
4553 &mut rng,
4554 );
4555 let lrproof_raw = extract_send_packet_at(&resp_actions, 1);
4556 let lrproof_pkt = RawPacket::unpack(&lrproof_raw).unwrap();
4557 let init_actions2 = init_mgr.handle_local_delivery(
4558 lrproof_pkt.destination_hash,
4559 &lrproof_raw,
4560 lrproof_pkt.packet_hash,
4561 rns_core::transport::types::InterfaceId(0),
4562 &mut rng,
4563 );
4564 let lrrtt_raw = extract_any_send_packet(&init_actions2);
4565 let lrrtt_pkt = RawPacket::unpack(&lrrtt_raw).unwrap();
4566 resp_mgr.handle_local_delivery(
4567 lrrtt_pkt.destination_hash,
4568 &lrrtt_raw,
4569 lrrtt_pkt.packet_hash,
4570 rns_core::transport::types::InterfaceId(0),
4571 &mut rng,
4572 );
4573
4574 let req_actions = init_mgr.send_request(&link_id, "/status", b"query", &mut rng);
4576 assert_eq!(req_actions.len(), 1);
4577
4578 let req_raw = extract_send_packet_from(&req_actions);
4580 let req_pkt = RawPacket::unpack(&req_raw).unwrap();
4581 let resp_actions = resp_mgr.handle_local_delivery(
4582 req_pkt.destination_hash,
4583 &req_raw,
4584 req_pkt.packet_hash,
4585 rns_core::transport::types::InterfaceId(0),
4586 &mut rng,
4587 );
4588
4589 let has_response = resp_actions
4591 .iter()
4592 .any(|a| matches!(a, LinkManagerAction::SendPacket { .. }));
4593 assert!(has_response, "Handler should produce a response packet");
4594 }
4595
4596 #[test]
4597 fn packet_request_at_exact_size_limit_is_dispatched() {
4598 use std::sync::{Arc, Mutex};
4599
4600 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
4601 let mut rng = OsRng;
4602 let observed = Arc::new(Mutex::new(false));
4603 resp_mgr.register_request_handler("/bounded", None, {
4604 let observed = Arc::clone(&observed);
4605 move |_link_id, _path, _data, _remote| {
4606 *observed.lock().unwrap() = true;
4607 None
4608 }
4609 });
4610
4611 let request_actions = init_mgr.send_request(&link_id, "/bounded", b"\xc0", &mut rng);
4612 let raw = extract_any_send_packet(&request_actions);
4613 let packet = RawPacket::unpack(&raw).unwrap();
4614 let packed_size = resp_mgr.links[&link_id]
4615 .engine
4616 .decrypt(&packet.data)
4617 .unwrap()
4618 .len();
4619 resp_mgr.links.get_mut(&link_id).unwrap().max_request_size = Some(packed_size);
4620
4621 resp_mgr.handle_local_delivery(
4622 packet.destination_hash,
4623 &raw,
4624 packet.packet_hash,
4625 rns_core::transport::types::InterfaceId(0),
4626 &mut rng,
4627 );
4628
4629 assert!(*observed.lock().unwrap());
4630 }
4631
4632 #[test]
4633 fn packet_request_one_byte_over_size_limit_is_ignored_before_dispatch() {
4634 use std::sync::{Arc, Mutex};
4635
4636 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
4637 let mut rng = OsRng;
4638 let observed = Arc::new(Mutex::new(false));
4639 resp_mgr.register_request_handler("/bounded", None, {
4640 let observed = Arc::clone(&observed);
4641 move |_link_id, _path, _data, _remote| {
4642 *observed.lock().unwrap() = true;
4643 None
4644 }
4645 });
4646
4647 let request_actions = init_mgr.send_request(&link_id, "/bounded", b"\xc0", &mut rng);
4648 let raw = extract_any_send_packet(&request_actions);
4649 let packet = RawPacket::unpack(&raw).unwrap();
4650 let packed_size = resp_mgr.links[&link_id]
4651 .engine
4652 .decrypt(&packet.data)
4653 .unwrap()
4654 .len();
4655 resp_mgr.links.get_mut(&link_id).unwrap().max_request_size = Some(packed_size - 1);
4656
4657 let actions = resp_mgr.handle_local_delivery(
4658 packet.destination_hash,
4659 &raw,
4660 packet.packet_hash,
4661 rns_core::transport::types::InterfaceId(0),
4662 &mut rng,
4663 );
4664
4665 assert!(actions.is_empty());
4666 assert!(!*observed.lock().unwrap());
4667 }
4668
4669 #[test]
4670 fn test_send_request_wraps_invalid_msgpack_data_as_bin() {
4671 use std::sync::{Arc, Mutex};
4672
4673 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
4674 let mut rng = OsRng;
4675
4676 let invalid = vec![0xC1];
4677 let expected = rns_core::msgpack::pack(&rns_core::msgpack::Value::Bin(invalid.clone()));
4678 let captured = Arc::new(Mutex::new(None::<Vec<u8>>));
4679 let captured_for_handler = Arc::clone(&captured);
4680
4681 resp_mgr.register_request_handler("/bin", None, move |_link_id, _path, data, _remote| {
4682 *captured_for_handler.lock().unwrap() = Some(data.to_vec());
4683 Some(rns_core::msgpack::pack(&rns_core::msgpack::Value::Bool(
4684 true,
4685 )))
4686 });
4687
4688 let req_actions = init_mgr.send_request(&link_id, "/bin", &invalid, &mut rng);
4689 let req_raw = extract_send_packet_from(&req_actions);
4690 let req_pkt = RawPacket::unpack(&req_raw).unwrap();
4691 let resp_actions = resp_mgr.handle_local_delivery(
4692 req_pkt.destination_hash,
4693 &req_raw,
4694 req_pkt.packet_hash,
4695 rns_core::transport::types::InterfaceId(0),
4696 &mut rng,
4697 );
4698
4699 assert!(
4700 resp_actions
4701 .iter()
4702 .any(|a| matches!(a, LinkManagerAction::SendPacket { .. })),
4703 "handler should still produce a response"
4704 );
4705 assert_eq!(*captured.lock().unwrap(), Some(expected));
4706 }
4707
4708 #[test]
4709 fn test_invalid_response_bytes_are_returned_as_msgpack_bin() {
4710 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
4711 let mut rng = OsRng;
4712 let invalid_response = vec![0xC1];
4713 let expected =
4714 rns_core::msgpack::pack(&rns_core::msgpack::Value::Bin(invalid_response.clone()));
4715
4716 resp_mgr.register_request_handler("/invalid-response", None, {
4717 let invalid_response = invalid_response.clone();
4718 move |_link_id, _path, _data, _remote| Some(invalid_response.clone())
4719 });
4720
4721 let req_actions = init_mgr.send_request(&link_id, "/invalid-response", b"\xc0", &mut rng);
4722 let req_raw = extract_send_packet_from(&req_actions);
4723 let req_pkt = RawPacket::unpack(&req_raw).unwrap();
4724 let resp_actions = resp_mgr.handle_local_delivery(
4725 req_pkt.destination_hash,
4726 &req_raw,
4727 req_pkt.packet_hash,
4728 rns_core::transport::types::InterfaceId(0),
4729 &mut rng,
4730 );
4731
4732 let resp_raw = extract_any_send_packet(&resp_actions);
4733 let resp_pkt = RawPacket::unpack(&resp_raw).unwrap();
4734 let init_actions = init_mgr.handle_local_delivery(
4735 resp_pkt.destination_hash,
4736 &resp_raw,
4737 resp_pkt.packet_hash,
4738 rns_core::transport::types::InterfaceId(0),
4739 &mut rng,
4740 );
4741
4742 let response_data = init_actions
4743 .iter()
4744 .find_map(|action| match action {
4745 LinkManagerAction::ResponseReceived { data, .. } => Some(data.clone()),
4746 _ => None,
4747 })
4748 .expect("initiator should receive a response");
4749 assert_eq!(response_data, expected);
4750 }
4751
4752 #[test]
4753 fn packet_response_at_exact_size_limit_is_accepted() {
4754 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
4755 let mut rng = OsRng;
4756 let payload = vec![0xA5; 64];
4757 let response_value =
4758 rns_core::msgpack::pack(&rns_core::msgpack::Value::Bin(payload.clone()));
4759 resp_mgr.register_request_handler("/bounded-response", None, {
4760 let response_value = response_value.clone();
4761 move |_, _, _, _| Some(response_value.clone())
4762 });
4763
4764 let request_actions = init_mgr.send_request_with_max_response_size(
4765 &link_id,
4766 "/bounded-response",
4767 b"\xc0",
4768 Some(payload.len()),
4769 &mut rng,
4770 );
4771 let request_raw = extract_any_send_packet(&request_actions);
4772 let request = RawPacket::unpack(&request_raw).unwrap();
4773 let response_actions = resp_mgr.handle_local_delivery(
4774 request.destination_hash,
4775 &request_raw,
4776 request.packet_hash,
4777 rns_core::transport::types::InterfaceId(0),
4778 &mut rng,
4779 );
4780 let response_raw = extract_any_send_packet(&response_actions);
4781 let response = RawPacket::unpack(&response_raw).unwrap();
4782 let actions = init_mgr.handle_local_delivery(
4783 response.destination_hash,
4784 &response_raw,
4785 response.packet_hash,
4786 rns_core::transport::types::InterfaceId(0),
4787 &mut rng,
4788 );
4789
4790 assert!(actions.iter().any(|action| matches!(
4791 action,
4792 LinkManagerAction::ResponseReceived { data, .. } if data == &response_value
4793 )));
4794 assert!(!actions
4795 .iter()
4796 .any(|action| matches!(action, LinkManagerAction::RequestFailed { .. })));
4797 }
4798
4799 #[test]
4800 fn packet_response_one_byte_over_size_limit_fails_request() {
4801 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
4802 let mut rng = OsRng;
4803 let payload = vec![0xA5; 64];
4804 let response_value =
4805 rns_core::msgpack::pack(&rns_core::msgpack::Value::Bin(payload.clone()));
4806 resp_mgr.register_request_handler("/bounded-response", None, {
4807 let response_value = response_value.clone();
4808 move |_, _, _, _| Some(response_value.clone())
4809 });
4810
4811 let request_actions = init_mgr.send_request_with_max_response_size(
4812 &link_id,
4813 "/bounded-response",
4814 b"\xc0",
4815 Some(payload.len() - 1),
4816 &mut rng,
4817 );
4818 let request_raw = extract_any_send_packet(&request_actions);
4819 let request = RawPacket::unpack(&request_raw).unwrap();
4820 let request_id = request.get_truncated_hash();
4821 let response_actions = resp_mgr.handle_local_delivery(
4822 request.destination_hash,
4823 &request_raw,
4824 request.packet_hash,
4825 rns_core::transport::types::InterfaceId(0),
4826 &mut rng,
4827 );
4828 let response_raw = extract_any_send_packet(&response_actions);
4829 let response = RawPacket::unpack(&response_raw).unwrap();
4830 let actions = init_mgr.handle_local_delivery(
4831 response.destination_hash,
4832 &response_raw,
4833 response.packet_hash,
4834 rns_core::transport::types::InterfaceId(0),
4835 &mut rng,
4836 );
4837
4838 assert!(actions.iter().any(|action| matches!(
4839 action,
4840 LinkManagerAction::RequestFailed {
4841 request_id: failed_id,
4842 reason: RequestFailure::ResponseTooLarge { size: 64, maximum: 63 },
4843 ..
4844 } if failed_id == &request_id
4845 )));
4846 assert!(!actions
4847 .iter()
4848 .any(|action| matches!(action, LinkManagerAction::ResponseReceived { .. })));
4849 assert!(!init_mgr.links[&link_id]
4850 .pending_requests
4851 .contains_key(&request_id));
4852 assert!(init_mgr
4853 .handle_local_delivery(
4854 response.destination_hash,
4855 &response_raw,
4856 response.packet_hash,
4857 rns_core::transport::types::InterfaceId(0),
4858 &mut rng,
4859 )
4860 .is_empty());
4861 }
4862
4863 #[test]
4864 fn test_request_acl_deny_unidentified() {
4865 let mut rng = OsRng;
4866 let dest_hash = [0xDD; 16];
4867 let mut resp_mgr = LinkManager::new();
4868 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
4869 resp_mgr.register_link_destination(
4870 dest_hash,
4871 sig_prv,
4872 sig_pub_bytes,
4873 ResourceStrategy::AcceptNone,
4874 );
4875
4876 resp_mgr.register_request_handler(
4878 "/restricted",
4879 Some(vec![[0xAA; 16]]),
4880 |_link_id, _path, _data, _remote| Some(b"secret".to_vec()),
4881 );
4882
4883 let mut init_mgr = LinkManager::new();
4884
4885 let (link_id, init_actions) = init_mgr.create_link(
4887 &dest_hash,
4888 &sig_pub_bytes,
4889 1,
4890 constants::MTU as u32,
4891 &mut rng,
4892 );
4893 let lr_raw = extract_send_packet(&init_actions);
4894 let lr_pkt = RawPacket::unpack(&lr_raw).unwrap();
4895 let resp_actions = resp_mgr.handle_local_delivery(
4896 lr_pkt.destination_hash,
4897 &lr_raw,
4898 lr_pkt.packet_hash,
4899 rns_core::transport::types::InterfaceId(0),
4900 &mut rng,
4901 );
4902 let lrproof_raw = extract_send_packet_at(&resp_actions, 1);
4903 let lrproof_pkt = RawPacket::unpack(&lrproof_raw).unwrap();
4904 let init_actions2 = init_mgr.handle_local_delivery(
4905 lrproof_pkt.destination_hash,
4906 &lrproof_raw,
4907 lrproof_pkt.packet_hash,
4908 rns_core::transport::types::InterfaceId(0),
4909 &mut rng,
4910 );
4911 let lrrtt_raw = extract_any_send_packet(&init_actions2);
4912 let lrrtt_pkt = RawPacket::unpack(&lrrtt_raw).unwrap();
4913 resp_mgr.handle_local_delivery(
4914 lrrtt_pkt.destination_hash,
4915 &lrrtt_raw,
4916 lrrtt_pkt.packet_hash,
4917 rns_core::transport::types::InterfaceId(0),
4918 &mut rng,
4919 );
4920
4921 let req_actions = init_mgr.send_request(&link_id, "/restricted", b"query", &mut rng);
4923 let req_raw = extract_send_packet_from(&req_actions);
4924 let req_pkt = RawPacket::unpack(&req_raw).unwrap();
4925 let resp_actions = resp_mgr.handle_local_delivery(
4926 req_pkt.destination_hash,
4927 &req_raw,
4928 req_pkt.packet_hash,
4929 rns_core::transport::types::InterfaceId(0),
4930 &mut rng,
4931 );
4932
4933 let has_response = resp_actions
4935 .iter()
4936 .any(|a| matches!(a, LinkManagerAction::SendPacket { .. }));
4937 assert!(!has_response, "Unidentified peer should be denied");
4938 }
4939
4940 #[test]
4941 fn test_teardown_link() {
4942 let mut rng = OsRng;
4943 let dest_hash = [0xDD; 16];
4944 let mut mgr = LinkManager::new();
4945
4946 let dummy_sig = [0xAA; 32];
4947 let (link_id, _) =
4948 mgr.create_link(&dest_hash, &dummy_sig, 1, constants::MTU as u32, &mut rng);
4949 assert_eq!(mgr.link_count(), 1);
4950
4951 let actions = mgr.teardown_link(&link_id);
4952 let has_close = actions
4953 .iter()
4954 .any(|a| matches!(a, LinkManagerAction::LinkClosed { .. }));
4955 assert!(has_close);
4956 assert!(!actions
4957 .iter()
4958 .any(|a| matches!(a, LinkManagerAction::SendPacket { .. })));
4959 assert!(mgr.teardown_link(&link_id).is_empty());
4960
4961 let tick_actions = mgr.tick(&mut rng);
4963 let has_deregister = tick_actions
4964 .iter()
4965 .any(|a| matches!(a, LinkManagerAction::DeregisterLinkDest { .. }));
4966 assert!(has_deregister);
4967 assert_eq!(mgr.link_count(), 0);
4968 }
4969
4970 #[test]
4971 fn active_teardown_sends_linkclose_and_remote_closed_teardown_is_noop() {
4972 let (mut initiator, mut responder, link_id) = setup_active_link();
4973 let actions = initiator.teardown_link(&link_id);
4974 let close_packet = actions
4975 .iter()
4976 .find_map(|action| match action {
4977 LinkManagerAction::SendPacket { raw, .. } => RawPacket::unpack(raw)
4978 .ok()
4979 .filter(|packet| packet.context == constants::CONTEXT_LINKCLOSE),
4980 _ => None,
4981 })
4982 .expect("active teardown must emit LINKCLOSE");
4983 assert!(close_packet.raw.len() > constants::HEADER_MINSIZE);
4984 let plaintext = responder
4985 .links
4986 .get(&link_id)
4987 .unwrap()
4988 .engine
4989 .decrypt(&close_packet.data)
4990 .expect("LINKCLOSE payload must authenticate");
4991 assert_eq!(plaintext, link_id);
4992 assert!(initiator.teardown_link(&link_id).is_empty());
4993
4994 responder
4995 .links
4996 .get_mut(&link_id)
4997 .unwrap()
4998 .engine
4999 .handle_teardown();
5000 assert!(responder.teardown_link(&link_id).is_empty());
5001 }
5002
5003 #[test]
5004 fn forged_or_empty_linkclose_does_not_close_an_active_link() {
5005 let (_initiator, mut responder, link_id) = setup_active_link();
5006 let flags = PacketFlags {
5007 header_type: constants::HEADER_1,
5008 context_flag: constants::FLAG_UNSET,
5009 transport_type: constants::TRANSPORT_BROADCAST,
5010 destination_type: constants::DESTINATION_LINK,
5011 packet_type: constants::PACKET_TYPE_DATA,
5012 };
5013 let mut rng = OsRng;
5014
5015 for forged_payload in [Vec::new(), vec![0x55; 48]] {
5016 let packet = RawPacket::pack(
5017 flags,
5018 0,
5019 &link_id,
5020 None,
5021 constants::CONTEXT_LINKCLOSE,
5022 &forged_payload,
5023 )
5024 .unwrap();
5025 let actions = responder.handle_local_delivery(
5026 link_id,
5027 &packet.raw,
5028 [0u8; 32],
5029 rns_core::transport::types::InterfaceId(1),
5030 &mut rng,
5031 );
5032
5033 assert_eq!(responder.link_state(&link_id), Some(LinkState::Active));
5034 assert!(!actions
5035 .iter()
5036 .any(|action| matches!(action, LinkManagerAction::LinkClosed { .. })));
5037 }
5038 }
5039
5040 #[test]
5041 fn test_identify_on_link() {
5042 let mut rng = OsRng;
5043 let dest_hash = [0xDD; 16];
5044 let mut resp_mgr = LinkManager::new();
5045 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
5046 resp_mgr.register_link_destination(
5047 dest_hash,
5048 sig_prv,
5049 sig_pub_bytes,
5050 ResourceStrategy::AcceptNone,
5051 );
5052 let mut init_mgr = LinkManager::new();
5053
5054 let (link_id, init_actions) = init_mgr.create_link(
5056 &dest_hash,
5057 &sig_pub_bytes,
5058 1,
5059 constants::MTU as u32,
5060 &mut rng,
5061 );
5062 let lr_raw = extract_send_packet(&init_actions);
5063 let lr_pkt = RawPacket::unpack(&lr_raw).unwrap();
5064 let resp_actions = resp_mgr.handle_local_delivery(
5065 lr_pkt.destination_hash,
5066 &lr_raw,
5067 lr_pkt.packet_hash,
5068 rns_core::transport::types::InterfaceId(0),
5069 &mut rng,
5070 );
5071 let lrproof_raw = extract_send_packet_at(&resp_actions, 1);
5072 let lrproof_pkt = RawPacket::unpack(&lrproof_raw).unwrap();
5073 let init_actions2 = init_mgr.handle_local_delivery(
5074 lrproof_pkt.destination_hash,
5075 &lrproof_raw,
5076 lrproof_pkt.packet_hash,
5077 rns_core::transport::types::InterfaceId(0),
5078 &mut rng,
5079 );
5080 let lrrtt_raw = extract_any_send_packet(&init_actions2);
5081 let lrrtt_pkt = RawPacket::unpack(&lrrtt_raw).unwrap();
5082 resp_mgr.handle_local_delivery(
5083 lrrtt_pkt.destination_hash,
5084 &lrrtt_raw,
5085 lrrtt_pkt.packet_hash,
5086 rns_core::transport::types::InterfaceId(0),
5087 &mut rng,
5088 );
5089
5090 let identity = Identity::new(&mut rng);
5092 let id_actions = init_mgr.identify(&link_id, &identity, &mut rng);
5093 assert_eq!(id_actions.len(), 1);
5094
5095 let id_raw = extract_send_packet_from(&id_actions);
5097 let id_pkt = RawPacket::unpack(&id_raw).unwrap();
5098 let resp_actions = resp_mgr.handle_local_delivery(
5099 id_pkt.destination_hash,
5100 &id_raw,
5101 id_pkt.packet_hash,
5102 rns_core::transport::types::InterfaceId(0),
5103 &mut rng,
5104 );
5105
5106 let has_identified = resp_actions
5107 .iter()
5108 .any(|a| matches!(a, LinkManagerAction::RemoteIdentified { .. }));
5109 assert!(has_identified, "Responder should emit RemoteIdentified");
5110 }
5111
5112 #[test]
5113 fn test_path_hash_computation() {
5114 let h1 = compute_path_hash("/status");
5115 let h2 = compute_path_hash("/path");
5116 assert_ne!(h1, h2);
5117
5118 assert_eq!(h1, compute_path_hash("/status"));
5120 }
5121
5122 #[test]
5123 fn test_link_count() {
5124 let mut mgr = LinkManager::new();
5125 let mut rng = OsRng;
5126
5127 assert_eq!(mgr.link_count(), 0);
5128
5129 let dummy_sig = [0xAA; 32];
5130 mgr.create_link(&[0x11; 16], &dummy_sig, 1, constants::MTU as u32, &mut rng);
5131 assert_eq!(mgr.link_count(), 1);
5132
5133 mgr.create_link(&[0x22; 16], &dummy_sig, 1, constants::MTU as u32, &mut rng);
5134 assert_eq!(mgr.link_count(), 2);
5135 }
5136
5137 fn extract_send_packet(actions: &[LinkManagerAction]) -> Vec<u8> {
5140 extract_send_packet_at(actions, actions.len() - 1)
5141 }
5142
5143 fn extract_send_packet_at(actions: &[LinkManagerAction], idx: usize) -> Vec<u8> {
5144 match &actions[idx] {
5145 LinkManagerAction::SendPacket { raw, .. } => raw.clone(),
5146 other => panic!("Expected SendPacket at index {}, got {:?}", idx, other),
5147 }
5148 }
5149
5150 fn extract_any_send_packet(actions: &[LinkManagerAction]) -> Vec<u8> {
5151 actions
5152 .iter()
5153 .find_map(|a| match a {
5154 LinkManagerAction::SendPacket { raw, .. } => Some(raw.clone()),
5155 _ => None,
5156 })
5157 .expect("Expected at least one SendPacket action")
5158 }
5159
5160 fn extract_send_packet_from(actions: &[LinkManagerAction]) -> Vec<u8> {
5161 extract_any_send_packet(actions)
5162 }
5163
5164 fn setup_active_link() -> (LinkManager, LinkManager, LinkId) {
5167 setup_active_link_with_max_request_size(None)
5168 }
5169
5170 fn setup_active_link_with_max_request_size(
5171 max_request_size: Option<usize>,
5172 ) -> (LinkManager, LinkManager, LinkId) {
5173 let mut rng = OsRng;
5174 let dest_hash = [0xDD; 16];
5175 let mut resp_mgr = LinkManager::new();
5176 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
5177 resp_mgr.register_link_destination(
5178 dest_hash,
5179 sig_prv,
5180 sig_pub_bytes,
5181 ResourceStrategy::AcceptNone,
5182 );
5183 assert!(resp_mgr.set_link_destination_max_request_size(&dest_hash, max_request_size));
5184 let mut init_mgr = LinkManager::new();
5185
5186 let (link_id, init_actions) = init_mgr.create_link(
5187 &dest_hash,
5188 &sig_pub_bytes,
5189 1,
5190 constants::MTU as u32,
5191 &mut rng,
5192 );
5193 let lr_raw = extract_send_packet(&init_actions);
5194 let lr_pkt = RawPacket::unpack(&lr_raw).unwrap();
5195 let resp_actions = resp_mgr.handle_local_delivery(
5196 lr_pkt.destination_hash,
5197 &lr_raw,
5198 lr_pkt.packet_hash,
5199 rns_core::transport::types::InterfaceId(0),
5200 &mut rng,
5201 );
5202 let lrproof_raw = extract_send_packet_at(&resp_actions, 1);
5203 let lrproof_pkt = RawPacket::unpack(&lrproof_raw).unwrap();
5204 let init_actions2 = init_mgr.handle_local_delivery(
5205 lrproof_pkt.destination_hash,
5206 &lrproof_raw,
5207 lrproof_pkt.packet_hash,
5208 rns_core::transport::types::InterfaceId(0),
5209 &mut rng,
5210 );
5211 let lrrtt_raw = extract_any_send_packet(&init_actions2);
5212 let lrrtt_pkt = RawPacket::unpack(&lrrtt_raw).unwrap();
5213 resp_mgr.handle_local_delivery(
5214 lrrtt_pkt.destination_hash,
5215 &lrrtt_raw,
5216 lrrtt_pkt.packet_hash,
5217 rns_core::transport::types::InterfaceId(0),
5218 &mut rng,
5219 );
5220
5221 assert_eq!(init_mgr.link_state(&link_id), Some(LinkState::Active));
5222 assert_eq!(resp_mgr.link_state(&link_id), Some(LinkState::Active));
5223
5224 (init_mgr, resp_mgr, link_id)
5225 }
5226
5227 #[test]
5228 fn request_size_limit_is_inherited_when_link_is_accepted() {
5229 let (_init_mgr, resp_mgr, link_id) = setup_active_link_with_max_request_size(Some(321));
5230 assert_eq!(resp_mgr.links[&link_id].max_request_size, Some(321));
5231 }
5232
5233 #[test]
5234 fn request_size_limit_updates_existing_responder_links() {
5235 let (_init_mgr, mut resp_mgr, link_id) = setup_active_link();
5236 let dest_hash = resp_mgr.links[&link_id].dest_hash;
5237 assert!(resp_mgr.set_link_destination_max_request_size(&dest_hash, Some(654)));
5238 assert_eq!(resp_mgr.links[&link_id].max_request_size, Some(654));
5239 }
5240
5241 #[test]
5242 fn setting_request_size_limit_for_unknown_destination_fails() {
5243 let mut mgr = LinkManager::new();
5244 assert!(!mgr.set_link_destination_max_request_size(&[0xEE; 16], Some(1)));
5245 }
5246
5247 fn packed_keepalive(link_id: &LinkId, payload: &[u8]) -> (Vec<u8>, RawPacket) {
5248 let flags = PacketFlags {
5249 header_type: constants::HEADER_1,
5250 context_flag: constants::FLAG_UNSET,
5251 transport_type: constants::TRANSPORT_BROADCAST,
5252 destination_type: constants::DESTINATION_LINK,
5253 packet_type: constants::PACKET_TYPE_DATA,
5254 };
5255 let (raw, _) = RawPacket::pack_raw_with_hash(
5256 flags,
5257 0,
5258 link_id,
5259 None,
5260 constants::CONTEXT_KEEPALIVE,
5261 payload,
5262 )
5263 .unwrap();
5264 let packet = RawPacket::unpack(&raw).unwrap();
5265 (raw, packet)
5266 }
5267
5268 fn keepalive_packets(actions: &[LinkManagerAction]) -> Vec<RawPacket> {
5269 actions
5270 .iter()
5271 .filter_map(|action| match action {
5272 LinkManagerAction::SendPacket { raw, .. } => RawPacket::unpack(raw).ok(),
5273 _ => None,
5274 })
5275 .filter(|packet| packet.context == constants::CONTEXT_KEEPALIVE)
5276 .collect()
5277 }
5278
5279 #[test]
5280 fn initiator_tick_emits_ff_keepalive_probe_when_outbound_is_quiet() {
5281 let (mut initiator, _, link_id) = setup_active_link();
5282 let now = time::now();
5283 let keepalive = initiator.links[&link_id].engine.keepalive_interval();
5284 let engine = &mut initiator.links.get_mut(&link_id).unwrap().engine;
5285 engine.record_inbound(now);
5286 engine.record_outbound(now - keepalive - 1.0, true);
5287
5288 let packets = keepalive_packets(&initiator.tick(&mut OsRng));
5289 assert_eq!(packets.len(), 1);
5290 assert_eq!(packets[0].data, [0xff]);
5291 }
5292
5293 #[test]
5294 fn responder_tick_never_originates_keepalive_probe() {
5295 let (_, mut responder, link_id) = setup_active_link();
5296 let now = time::now();
5297 let keepalive = responder.links[&link_id].engine.keepalive_interval();
5298 let engine = &mut responder.links.get_mut(&link_id).unwrap().engine;
5299 engine.record_inbound(now - keepalive - 1.0);
5300 engine.record_outbound(now - keepalive - 1.0, false);
5301
5302 assert!(keepalive_packets(&responder.tick(&mut OsRng)).is_empty());
5303 }
5304
5305 #[test]
5306 fn responder_acknowledges_ff_probe_after_outbound_silence() {
5307 let (_, mut responder, link_id) = setup_active_link();
5308 let now = time::now();
5309 let keepalive = responder.links[&link_id].engine.keepalive_interval();
5310 responder
5311 .links
5312 .get_mut(&link_id)
5313 .unwrap()
5314 .engine
5315 .record_outbound(now - keepalive - 1.0, false);
5316 let (raw, packet) = packed_keepalive(&link_id, &[0xff]);
5317
5318 let actions = responder.handle_local_delivery(
5319 packet.destination_hash,
5320 &raw,
5321 packet.packet_hash,
5322 rns_core::transport::types::InterfaceId(0),
5323 &mut OsRng,
5324 );
5325 let packets = keepalive_packets(&actions);
5326 assert_eq!(packets.len(), 1);
5327 assert_eq!(packets[0].data, [0xfe]);
5328 }
5329
5330 #[test]
5331 fn recent_responder_traffic_suppresses_keepalive_ack() {
5332 let (_, mut responder, link_id) = setup_active_link();
5333 responder
5334 .links
5335 .get_mut(&link_id)
5336 .unwrap()
5337 .engine
5338 .record_outbound(time::now(), false);
5339 let (raw, packet) = packed_keepalive(&link_id, &[0xff]);
5340
5341 let actions = responder.handle_local_delivery(
5342 packet.destination_hash,
5343 &raw,
5344 packet.packet_hash,
5345 rns_core::transport::types::InterfaceId(0),
5346 &mut OsRng,
5347 );
5348 assert!(keepalive_packets(&actions).is_empty());
5349 }
5350
5351 #[test]
5352 fn keepalive_ack_does_not_create_ping_pong_reply() {
5353 let (mut initiator, _, link_id) = setup_active_link();
5354 let (raw, packet) = packed_keepalive(&link_id, &[0xfe]);
5355
5356 let actions = initiator.handle_local_delivery(
5357 packet.destination_hash,
5358 &raw,
5359 packet.packet_hash,
5360 rns_core::transport::types::InterfaceId(0),
5361 &mut OsRng,
5362 );
5363 assert!(keepalive_packets(&actions).is_empty());
5364 }
5365
5366 #[test]
5371 fn test_resource_strategy_default() {
5372 let mut mgr = LinkManager::new();
5373 let mut rng = OsRng;
5374 let dummy_sig = [0xAA; 32];
5375 let (link_id, _) =
5376 mgr.create_link(&[0x11; 16], &dummy_sig, 1, constants::MTU as u32, &mut rng);
5377
5378 let link = mgr.links.get(&link_id).unwrap();
5380 assert_eq!(link.resource_strategy, ResourceStrategy::AcceptNone);
5381 }
5382
5383 #[test]
5384 fn test_set_resource_strategy() {
5385 let mut mgr = LinkManager::new();
5386 let mut rng = OsRng;
5387 let dummy_sig = [0xAA; 32];
5388 let (link_id, _) =
5389 mgr.create_link(&[0x11; 16], &dummy_sig, 1, constants::MTU as u32, &mut rng);
5390
5391 mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
5392 assert_eq!(
5393 mgr.links.get(&link_id).unwrap().resource_strategy,
5394 ResourceStrategy::AcceptAll
5395 );
5396
5397 mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptApp);
5398 assert_eq!(
5399 mgr.links.get(&link_id).unwrap().resource_strategy,
5400 ResourceStrategy::AcceptApp
5401 );
5402 }
5403
5404 #[test]
5405 fn test_send_resource_on_active_link() {
5406 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
5407 let mut rng = OsRng;
5408
5409 let data = vec![0xAB; 100]; let actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
5412
5413 let has_send = actions
5415 .iter()
5416 .any(|a| matches!(a, LinkManagerAction::SendPacket { .. }));
5417 assert!(
5418 has_send,
5419 "send_resource should emit advertisement SendPacket"
5420 );
5421 }
5422
5423 #[test]
5424 fn test_large_send_request_uses_request_resource() {
5425 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
5426 let mut rng = OsRng;
5427
5428 let data = vec![0xAB; 2048];
5429 let actions = init_mgr.send_request(&link_id, "/large", &data, &mut rng);
5430
5431 let adv = first_resource_advertisement(&init_mgr, &link_id, &actions);
5432 assert!(adv.is_request());
5433 assert!(!adv.is_response());
5434 assert_eq!(adv.request_id.as_ref().map(Vec::len), Some(16));
5435 let request_id = LinkManager::response_request_id(&adv.request_id).unwrap();
5436 assert_eq!(
5437 init_mgr.links[&link_id]
5438 .pending_requests
5439 .get(&request_id)
5440 .and_then(|request| request.deadline),
5441 None,
5442 "resource request timeout must wait for delivery proof"
5443 );
5444 assert!(init_mgr.links[&link_id]
5445 .pending_requests
5446 .contains_key(&request_id));
5447
5448 let has_request_packet = actions.iter().any(|action| match action {
5449 LinkManagerAction::SendPacket { raw, .. } => RawPacket::unpack(raw)
5450 .map(|pkt| pkt.context == constants::CONTEXT_REQUEST)
5451 .unwrap_or(false),
5452 _ => false,
5453 });
5454 assert!(!has_request_packet);
5455 }
5456
5457 #[test]
5458 fn unanswered_packet_request_expires_from_pending_set() {
5459 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
5460 let mut rng = OsRng;
5461 let actions = init_mgr.send_request(&link_id, "/no-response", b"\xc0", &mut rng);
5462 let raw = extract_any_send_packet(&actions);
5463 let request_id = RawPacket::unpack(&raw).unwrap().get_truncated_hash();
5464 assert!(init_mgr.links[&link_id]
5465 .pending_requests
5466 .get(&request_id)
5467 .is_some_and(|request| request.deadline.is_some()));
5468
5469 init_mgr
5470 .links
5471 .get_mut(&link_id)
5472 .unwrap()
5473 .pending_requests
5474 .get_mut(&request_id)
5475 .unwrap()
5476 .deadline = Some(time::now() - 1.0);
5477 init_mgr.tick(&mut rng);
5478
5479 assert!(!init_mgr.links[&link_id]
5480 .pending_requests
5481 .contains_key(&request_id));
5482 }
5483
5484 #[test]
5485 fn invalid_resource_advertisement_tears_down_link_once() {
5486 let (_init_mgr, mut resp_mgr, link_id) = setup_active_link();
5487 let mut rng = OsRng;
5488
5489 let actions = resp_mgr.handle_resource_adv(&link_id, &[0xc1], &mut rng);
5490
5491 assert_eq!(resp_mgr.link_state(&link_id), Some(LinkState::Closed));
5492 assert_eq!(
5493 actions
5494 .iter()
5495 .filter(|action| matches!(action, LinkManagerAction::LinkClosed { .. }))
5496 .count(),
5497 1
5498 );
5499 assert!(resp_mgr
5500 .handle_resource_adv(&link_id, &[0xc1], &mut rng)
5501 .is_empty());
5502 }
5503
5504 #[test]
5505 fn oversized_resource_advertisement_tears_down_link() {
5506 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
5507 let mut rng = OsRng;
5508 let adv_actions = init_mgr.send_resource_with_auto_compress(
5509 &link_id,
5510 &deterministic_bytes(1024),
5511 None,
5512 false,
5513 &mut rng,
5514 );
5515 let mut adv = first_resource_advertisement(&init_mgr, &link_id, &adv_actions);
5516 adv.transfer_size = (constants::RESOURCE_MAX_EFFICIENT_SIZE * 3 + 1) as u64;
5517
5518 let actions = resp_mgr.handle_resource_adv(&link_id, &adv.pack(0), &mut rng);
5519
5520 assert_eq!(resp_mgr.link_state(&link_id), Some(LinkState::Closed));
5521 assert!(actions
5522 .iter()
5523 .any(|action| matches!(action, LinkManagerAction::LinkClosed { .. })));
5524 }
5525
5526 #[test]
5527 fn request_resource_without_any_handler_is_ignored() {
5528 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
5529 let mut rng = OsRng;
5530 let data = deterministic_bytes(4096);
5531
5532 let adv_actions = init_mgr.send_request(&link_id, "/missing", &data, &mut rng);
5533 let adv_raw = extract_any_send_packet(&adv_actions);
5534 let adv_pkt = RawPacket::unpack(&adv_raw).unwrap();
5535 assert_eq!(adv_pkt.context, constants::CONTEXT_RESOURCE_ADV);
5536
5537 let actions = resp_mgr.handle_local_delivery(
5538 adv_pkt.destination_hash,
5539 &adv_raw,
5540 adv_pkt.packet_hash,
5541 rns_core::transport::types::InterfaceId(0),
5542 &mut rng,
5543 );
5544
5545 assert!(
5546 actions.is_empty(),
5547 "unhandled request resource must be ignored"
5548 );
5549 assert!(resp_mgr.links[&link_id].incoming_resources.is_empty());
5550 }
5551
5552 #[test]
5553 fn request_resource_at_exact_declared_size_limit_is_accepted() {
5554 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
5555 let mut rng = OsRng;
5556 resp_mgr.register_request_handler("/bounded", None, |_, _, _, _| None);
5557 let request = deterministic_bytes(4096);
5558 let adv_actions = init_mgr.send_request(&link_id, "/bounded", &request, &mut rng);
5559 let adv = first_resource_advertisement(&init_mgr, &link_id, &adv_actions);
5560 resp_mgr.links.get_mut(&link_id).unwrap().max_request_size = Some(adv.data_size as usize);
5561 let raw = extract_any_send_packet(&adv_actions);
5562 let packet = RawPacket::unpack(&raw).unwrap();
5563
5564 let actions = resp_mgr.handle_local_delivery(
5565 packet.destination_hash,
5566 &raw,
5567 packet.packet_hash,
5568 rns_core::transport::types::InterfaceId(0),
5569 &mut rng,
5570 );
5571
5572 assert!(
5573 !actions.is_empty(),
5574 "accepted resource should request parts"
5575 );
5576 assert_eq!(resp_mgr.links[&link_id].incoming_resources.len(), 1);
5577 }
5578
5579 #[test]
5580 fn request_resource_one_byte_over_declared_size_limit_is_rejected() {
5581 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
5582 let mut rng = OsRng;
5583 resp_mgr.register_request_handler("/bounded", None, |_, _, _, _| None);
5584 let adv_actions =
5585 init_mgr.send_request(&link_id, "/bounded", &deterministic_bytes(4096), &mut rng);
5586 let adv = first_resource_advertisement(&init_mgr, &link_id, &adv_actions);
5587 resp_mgr.links.get_mut(&link_id).unwrap().max_request_size =
5588 Some(adv.data_size as usize - 1);
5589 let raw = extract_any_send_packet(&adv_actions);
5590 let packet = RawPacket::unpack(&raw).unwrap();
5591
5592 let actions = resp_mgr.handle_local_delivery(
5593 packet.destination_hash,
5594 &raw,
5595 packet.packet_hash,
5596 rns_core::transport::types::InterfaceId(0),
5597 &mut rng,
5598 );
5599
5600 assert!(!actions.is_empty(), "rejected resource should send an RCL");
5601 assert!(resp_mgr.links[&link_id].incoming_resources.is_empty());
5602 }
5603
5604 #[test]
5605 fn request_size_limit_does_not_reject_ordinary_resources() {
5606 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
5607 let mut rng = OsRng;
5608 let link = resp_mgr.links.get_mut(&link_id).unwrap();
5609 link.max_request_size = Some(0);
5610 link.resource_strategy = ResourceStrategy::AcceptAll;
5611 let adv_actions = init_mgr.send_resource_with_auto_compress(
5612 &link_id,
5613 &deterministic_bytes(4096),
5614 None,
5615 false,
5616 &mut rng,
5617 );
5618 let raw = extract_any_send_packet(&adv_actions);
5619 let packet = RawPacket::unpack(&raw).unwrap();
5620
5621 let actions = resp_mgr.handle_local_delivery(
5622 packet.destination_hash,
5623 &raw,
5624 packet.packet_hash,
5625 rns_core::transport::types::InterfaceId(0),
5626 &mut rng,
5627 );
5628
5629 assert!(!actions.is_empty(), "ordinary resource should be accepted");
5630 assert_eq!(resp_mgr.links[&link_id].incoming_resources.len(), 1);
5631 }
5632
5633 #[test]
5634 fn unsolicited_response_resource_is_ignored() {
5635 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
5636 let mut rng = OsRng;
5637 let unsolicited_id = [0xEF; 16];
5638
5639 let adv_actions = resp_mgr.send_response_resource(
5640 &link_id,
5641 &unsolicited_id,
5642 &deterministic_bytes(4096),
5643 None,
5644 false,
5645 &mut rng,
5646 );
5647 let adv_raw = extract_any_send_packet(&adv_actions);
5648 let adv_pkt = RawPacket::unpack(&adv_raw).unwrap();
5649 let actions = init_mgr.handle_local_delivery(
5650 adv_pkt.destination_hash,
5651 &adv_raw,
5652 adv_pkt.packet_hash,
5653 rns_core::transport::types::InterfaceId(0),
5654 &mut rng,
5655 );
5656
5657 assert!(
5658 actions.is_empty(),
5659 "unsolicited response resource must be ignored"
5660 );
5661 assert!(init_mgr.links[&link_id].incoming_resources.is_empty());
5662 }
5663
5664 #[test]
5665 fn resource_packets_are_suppressed_after_link_closes() {
5666 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
5667 let mut rng = OsRng;
5668 init_mgr.send_resource_with_auto_compress(
5669 &link_id,
5670 &deterministic_bytes(4096),
5671 None,
5672 false,
5673 &mut rng,
5674 );
5675 init_mgr
5676 .links
5677 .get_mut(&link_id)
5678 .unwrap()
5679 .engine
5680 .handle_teardown();
5681
5682 for action in [
5683 ResourceAction::SendAdvertisement(vec![1]),
5684 ResourceAction::SendPart(vec![2]),
5685 ResourceAction::SendRequest(vec![3]),
5686 ResourceAction::SendHmu(vec![4]),
5687 ResourceAction::SendProof(vec![5; 64]),
5688 ] {
5689 let emitted = init_mgr.process_resource_actions(&link_id, vec![action], &mut rng);
5690 assert!(
5691 !emitted
5692 .iter()
5693 .any(|item| matches!(item, LinkManagerAction::SendPacket { .. })),
5694 "closed link emitted a resource packet"
5695 );
5696 }
5697 assert!(init_mgr.links[&link_id]
5698 .outgoing_resources
5699 .iter()
5700 .all(|sender| sender.status == rns_core::resource::ResourceStatus::Failed));
5701 }
5702
5703 #[test]
5704 fn large_request_resource_reaches_registered_handler_and_returns_response() {
5705 use std::sync::{Arc, Mutex};
5706
5707 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
5708 let mut rng = OsRng;
5709 let request_value =
5710 rns_core::msgpack::pack(&rns_core::msgpack::Value::Bin(deterministic_bytes(4096)));
5711 let observed = Arc::new(Mutex::new(None));
5712 resp_mgr.register_request_handler("/large-request", None, {
5713 let observed = Arc::clone(&observed);
5714 move |_link_id, _path, data, _remote| {
5715 *observed.lock().unwrap() = Some(data.to_vec());
5716 Some(rns_core::msgpack::pack(&rns_core::msgpack::Value::Bool(
5717 true,
5718 )))
5719 }
5720 });
5721
5722 let initial = init_mgr.send_request(&link_id, "/large-request", &request_value, &mut rng);
5723 let mut pending: Vec<(char, LinkManagerAction)> =
5724 initial.into_iter().map(|action| ('i', action)).collect();
5725 let mut response = None;
5726
5727 for _ in 0..300 {
5728 if pending.is_empty() || response.is_some() {
5729 break;
5730 }
5731 let mut next = Vec::new();
5732 for (source, action) in pending.drain(..) {
5733 let LinkManagerAction::SendPacket { raw, .. } = action else {
5734 continue;
5735 };
5736 let packet = RawPacket::unpack(&raw).unwrap();
5737 let actions = if source == 'i' {
5738 resp_mgr.handle_local_delivery(
5739 packet.destination_hash,
5740 &raw,
5741 packet.packet_hash,
5742 rns_core::transport::types::InterfaceId(0),
5743 &mut rng,
5744 )
5745 } else {
5746 init_mgr.handle_local_delivery(
5747 packet.destination_hash,
5748 &raw,
5749 packet.packet_hash,
5750 rns_core::transport::types::InterfaceId(0),
5751 &mut rng,
5752 )
5753 };
5754 for action in &actions {
5755 if let LinkManagerAction::ResponseReceived { data, .. } = action {
5756 response = Some(data.clone());
5757 }
5758 assert!(!matches!(
5759 action,
5760 LinkManagerAction::ResourceAcceptQuery { .. }
5761 ));
5762 }
5763 let next_source = if source == 'i' { 'r' } else { 'i' };
5764 next.extend(actions.into_iter().map(|action| (next_source, action)));
5765 }
5766 pending = next;
5767 }
5768
5769 assert_eq!(*observed.lock().unwrap(), Some(request_value));
5770 assert_eq!(
5771 response,
5772 Some(rns_core::msgpack::pack(&rns_core::msgpack::Value::Bool(
5773 true
5774 )))
5775 );
5776 assert!(init_mgr.links[&link_id].pending_requests.is_empty());
5777 }
5778
5779 fn first_resource_advertisement(
5780 mgr: &LinkManager,
5781 link_id: &LinkId,
5782 actions: &[LinkManagerAction],
5783 ) -> rns_core::resource::ResourceAdvertisement {
5784 let adv_raw = actions
5785 .iter()
5786 .find_map(|action| match action {
5787 LinkManagerAction::SendPacket { raw, .. } => {
5788 let pkt = RawPacket::unpack(raw).ok()?;
5789 (pkt.context == constants::CONTEXT_RESOURCE_ADV).then_some(raw)
5790 }
5791 _ => None,
5792 })
5793 .expect("sender should emit a resource advertisement");
5794 let adv_pkt = RawPacket::unpack(adv_raw).unwrap();
5795 let plaintext = mgr
5796 .links
5797 .get(link_id)
5798 .unwrap()
5799 .engine
5800 .decrypt(&adv_pkt.data)
5801 .unwrap();
5802 rns_core::resource::ResourceAdvertisement::unpack(&plaintext).unwrap()
5803 }
5804
5805 fn deterministic_bytes(len: usize) -> Vec<u8> {
5806 let mut state = 0x1234_5678u32;
5807 (0..len)
5808 .map(|_| {
5809 state = state.wrapping_mul(1664525).wrapping_add(1013904223);
5810 (state >> 16) as u8
5811 })
5812 .collect()
5813 }
5814
5815 struct CountingReader {
5816 inner: std::io::Cursor<Vec<u8>>,
5817 bytes_read: std::sync::Arc<std::sync::atomic::AtomicUsize>,
5818 }
5819
5820 impl Read for CountingReader {
5821 fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
5822 let count = self.inner.read(buffer)?;
5823 self.bytes_read
5824 .fetch_add(count, std::sync::atomic::Ordering::Relaxed);
5825 Ok(count)
5826 }
5827 }
5828
5829 #[test]
5830 fn streaming_resource_reads_one_segment_at_a_time_and_receives_to_disk() {
5831 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
5832 let mut rng = OsRng;
5833 let directory = tempfile::tempdir().unwrap();
5834 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
5835 resp_mgr.set_resource_receive_mode(
5836 &link_id,
5837 ResourceReceiveMode::TemporaryFile {
5838 directory: directory.path().to_path_buf(),
5839 max_bytes: None,
5840 },
5841 );
5842
5843 let data = deterministic_bytes(constants::RESOURCE_MAX_EFFICIENT_SIZE * 2 + 12345);
5844 let bytes_read = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
5845 let reader = CountingReader {
5846 inner: std::io::Cursor::new(data.clone()),
5847 bytes_read: bytes_read.clone(),
5848 };
5849 let initial = init_mgr.send_resource_stream(
5850 &link_id,
5851 ResourceTransferId(42),
5852 Box::new(reader),
5853 data.len() as u64,
5854 None,
5855 false,
5856 &mut rng,
5857 );
5858
5859 assert_eq!(
5860 bytes_read.load(std::sync::atomic::Ordering::Relaxed),
5861 constants::RESOURCE_MAX_EFFICIENT_SIZE,
5862 "starting a stream must not consume later segments"
5863 );
5864 assert_eq!(init_mgr.links[&link_id].outgoing_resources.len(), 1);
5865
5866 let (received, completed, _progress, failures, _rounds) = drive_link_manager_packets(
5867 &mut init_mgr,
5868 &mut resp_mgr,
5869 initial,
5870 'i',
5871 &mut rng,
5872 30_000,
5873 );
5874 assert_eq!(received, Some(data));
5875 assert!(completed);
5876 assert!(
5877 failures.is_empty(),
5878 "streaming transfer failed: {failures:?}"
5879 );
5880 assert_eq!(
5881 bytes_read.load(std::sync::atomic::Ordering::Relaxed),
5882 constants::RESOURCE_MAX_EFFICIENT_SIZE * 2 + 12345
5883 );
5884 assert_eq!(std::fs::read_dir(directory.path()).unwrap().count(), 0);
5885 }
5886
5887 fn drive_link_manager_packets(
5888 init_mgr: &mut LinkManager,
5889 resp_mgr: &mut LinkManager,
5890 initial_actions: Vec<LinkManagerAction>,
5891 initial_source: char,
5892 rng: &mut dyn Rng,
5893 max_rounds: usize,
5894 ) -> (
5895 Option<Vec<u8>>,
5896 bool,
5897 Vec<(char, usize, usize)>,
5898 Vec<(char, String)>,
5899 usize,
5900 ) {
5901 let mut pending: Vec<(char, LinkManagerAction)> = initial_actions
5902 .into_iter()
5903 .map(|a| (initial_source, a))
5904 .collect();
5905 let mut rounds = 0;
5906 let mut received_data = None;
5907 let mut sender_completed = false;
5908 let mut progress = Vec::new();
5909 let mut failures = Vec::new();
5910
5911 while !pending.is_empty() && rounds < max_rounds {
5912 rounds += 1;
5913 let mut next = Vec::new();
5914 for (source, action) in pending.drain(..) {
5915 let LinkManagerAction::SendPacket { raw, .. } = action else {
5916 continue;
5917 };
5918 let pkt = RawPacket::unpack(&raw).unwrap();
5919 let target_actions = if source == 'i' {
5920 resp_mgr.handle_local_delivery(
5921 pkt.destination_hash,
5922 &raw,
5923 pkt.packet_hash,
5924 rns_core::transport::types::InterfaceId(0),
5925 rng,
5926 )
5927 } else {
5928 init_mgr.handle_local_delivery(
5929 pkt.destination_hash,
5930 &raw,
5931 pkt.packet_hash,
5932 rns_core::transport::types::InterfaceId(0),
5933 rng,
5934 )
5935 };
5936 let target_source = if source == 'i' { 'r' } else { 'i' };
5937 for action in &target_actions {
5938 match action {
5939 LinkManagerAction::ResourceReceived { data, .. } => {
5940 received_data = Some(data.clone());
5941 }
5942 LinkManagerAction::ResourceFileReceived { resource, .. } => {
5943 received_data = Some(std::fs::read(resource.path()).unwrap());
5944 }
5945 LinkManagerAction::ResourceCompleted { .. } => {
5946 sender_completed = true;
5947 }
5948 LinkManagerAction::ResourceStreamCompleted { .. } => {
5949 sender_completed = true;
5950 }
5951 LinkManagerAction::ResourceProgress {
5952 received, total, ..
5953 } => {
5954 progress.push((target_source, *received, *total));
5955 }
5956 LinkManagerAction::ResourceFailed { error, .. } => {
5957 failures.push((target_source, error.clone()));
5958 }
5959 LinkManagerAction::ResourceStreamFailed { error, .. } => {
5960 failures.push((target_source, format!("{error:?}")));
5961 }
5962 _ => {}
5963 }
5964 }
5965 next.extend(target_actions.into_iter().map(|a| (target_source, a)));
5966 }
5967 pending = next;
5968 }
5969
5970 (received_data, sender_completed, progress, failures, rounds)
5971 }
5972
5973 #[test]
5974 fn test_send_resource_auto_compress_option_controls_adv_flag() {
5975 let data = vec![0x41; 2048];
5976
5977 let (mut compressed_mgr, _resp_mgr, link_id) = setup_active_link();
5978 let mut rng = OsRng;
5979 let actions =
5980 compressed_mgr.send_resource_with_auto_compress(&link_id, &data, None, true, &mut rng);
5981 let adv = first_resource_advertisement(&compressed_mgr, &link_id, &actions);
5982 assert!(
5983 adv.flags.compressed,
5984 "compressible resource should compress"
5985 );
5986
5987 let (mut plain_mgr, _resp_mgr, link_id) = setup_active_link();
5988 let mut rng = OsRng;
5989 let actions =
5990 plain_mgr.send_resource_with_auto_compress(&link_id, &data, None, false, &mut rng);
5991 let adv = first_resource_advertisement(&plain_mgr, &link_id, &actions);
5992 assert!(
5993 !adv.flags.compressed,
5994 "auto_compress=false should keep resource uncompressed"
5995 );
5996 }
5997
5998 #[test]
5999 fn test_send_resource_on_inactive_link() {
6000 let mut mgr = LinkManager::new();
6001 let mut rng = OsRng;
6002 let dummy_sig = [0xAA; 32];
6003 let (link_id, _) =
6004 mgr.create_link(&[0x11; 16], &dummy_sig, 1, constants::MTU as u32, &mut rng);
6005
6006 let actions = mgr.send_resource(&link_id, b"data", None, &mut rng);
6008 assert!(actions.is_empty(), "Cannot send resource on inactive link");
6009 }
6010
6011 #[test]
6012 fn test_send_resource_without_session_key_uses_encrypt_fallback_path() {
6013 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
6014 let mut rng = OsRng;
6015 init_mgr
6016 .links
6017 .get_mut(&link_id)
6018 .unwrap()
6019 .engine
6020 .clear_session_for_testing();
6021
6022 let actions = init_mgr.send_resource(&link_id, b"data", None, &mut rng);
6023
6024 assert!(
6025 actions.is_empty(),
6026 "without a session key, no advertisement should be emitted"
6027 );
6028 assert_eq!(
6029 init_mgr
6030 .links
6031 .get(&link_id)
6032 .map(|managed| managed.outgoing_resources.len()),
6033 Some(1)
6034 );
6035 }
6036
6037 #[test]
6038 fn test_resource_adv_rejected_by_accept_none() {
6039 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
6040 let mut rng = OsRng;
6041
6042 let data = vec![0xCD; 100];
6045 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
6046
6047 for action in &adv_actions {
6049 if let LinkManagerAction::SendPacket { raw, .. } = action {
6050 let pkt = RawPacket::unpack(raw).unwrap();
6051 let resp_actions = resp_mgr.handle_local_delivery(
6052 pkt.destination_hash,
6053 raw,
6054 pkt.packet_hash,
6055 rns_core::transport::types::InterfaceId(0),
6056 &mut rng,
6057 );
6058 let has_resource_received = resp_actions
6060 .iter()
6061 .any(|a| matches!(a, LinkManagerAction::ResourceReceived { .. }));
6062 assert!(
6063 !has_resource_received,
6064 "AcceptNone should not accept resource"
6065 );
6066 }
6067 }
6068 }
6069
6070 #[test]
6071 fn test_resource_adv_accepted_by_accept_all() {
6072 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
6073 let mut rng = OsRng;
6074
6075 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
6077
6078 let data = vec![0xCD; 100];
6080 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
6081
6082 for action in &adv_actions {
6084 if let LinkManagerAction::SendPacket { raw, .. } = action {
6085 let pkt = RawPacket::unpack(raw).unwrap();
6086 let resp_actions = resp_mgr.handle_local_delivery(
6087 pkt.destination_hash,
6088 raw,
6089 pkt.packet_hash,
6090 rns_core::transport::types::InterfaceId(0),
6091 &mut rng,
6092 );
6093 let has_send = resp_actions
6095 .iter()
6096 .any(|a| matches!(a, LinkManagerAction::SendPacket { .. }));
6097 assert!(has_send, "AcceptAll should accept and request parts");
6098 }
6099 }
6100 }
6101
6102 #[test]
6103 fn test_resource_accept_app_query() {
6104 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
6105 let mut rng = OsRng;
6106
6107 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptApp);
6109
6110 let data = vec![0xCD; 100];
6112 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
6113
6114 let mut got_query = false;
6116 for action in &adv_actions {
6117 if let LinkManagerAction::SendPacket { raw, .. } = action {
6118 let pkt = RawPacket::unpack(raw).unwrap();
6119 let resp_actions = resp_mgr.handle_local_delivery(
6120 pkt.destination_hash,
6121 raw,
6122 pkt.packet_hash,
6123 rns_core::transport::types::InterfaceId(0),
6124 &mut rng,
6125 );
6126 for a in &resp_actions {
6127 if matches!(a, LinkManagerAction::ResourceAcceptQuery { .. }) {
6128 got_query = true;
6129 }
6130 }
6131 }
6132 }
6133 assert!(got_query, "AcceptApp should emit ResourceAcceptQuery");
6134 }
6135
6136 #[test]
6137 fn test_resource_accept_app_accept() {
6138 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
6139 let mut rng = OsRng;
6140
6141 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptApp);
6142
6143 let data = vec![0xCD; 100];
6144 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
6145
6146 for action in &adv_actions {
6147 if let LinkManagerAction::SendPacket { raw, .. } = action {
6148 let pkt = RawPacket::unpack(raw).unwrap();
6149 let resp_actions = resp_mgr.handle_local_delivery(
6150 pkt.destination_hash,
6151 raw,
6152 pkt.packet_hash,
6153 rns_core::transport::types::InterfaceId(0),
6154 &mut rng,
6155 );
6156 for a in &resp_actions {
6157 if let LinkManagerAction::ResourceAcceptQuery {
6158 link_id: lid,
6159 resource_hash,
6160 ..
6161 } = a
6162 {
6163 let accept_actions =
6165 resp_mgr.accept_resource(lid, resource_hash, true, &mut rng);
6166 let has_send = accept_actions
6168 .iter()
6169 .any(|a| matches!(a, LinkManagerAction::SendPacket { .. }));
6170 assert!(
6171 has_send,
6172 "Accepting resource should produce request for parts"
6173 );
6174 }
6175 }
6176 }
6177 }
6178 }
6179
6180 #[test]
6181 fn test_resource_accept_app_reject() {
6182 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
6183 let mut rng = OsRng;
6184
6185 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptApp);
6186
6187 let data = vec![0xCD; 100];
6188 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
6189
6190 for action in &adv_actions {
6191 if let LinkManagerAction::SendPacket { raw, .. } = action {
6192 let pkt = RawPacket::unpack(raw).unwrap();
6193 let resp_actions = resp_mgr.handle_local_delivery(
6194 pkt.destination_hash,
6195 raw,
6196 pkt.packet_hash,
6197 rns_core::transport::types::InterfaceId(0),
6198 &mut rng,
6199 );
6200 for a in &resp_actions {
6201 if let LinkManagerAction::ResourceAcceptQuery {
6202 link_id: lid,
6203 resource_hash,
6204 ..
6205 } = a
6206 {
6207 let reject_actions =
6209 resp_mgr.accept_resource(lid, resource_hash, false, &mut rng);
6210 let has_resource_received = reject_actions
6213 .iter()
6214 .any(|a| matches!(a, LinkManagerAction::ResourceReceived { .. }));
6215 assert!(!has_resource_received);
6216 }
6217 }
6218 }
6219 }
6220 }
6221
6222 #[test]
6223 fn test_resource_full_transfer() {
6224 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
6225 let mut rng = OsRng;
6226
6227 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
6229
6230 let original_data = b"Hello, Resource Transfer!".to_vec();
6232 let adv_actions = init_mgr.send_resource(&link_id, &original_data, None, &mut rng);
6233
6234 let mut pending: Vec<(char, LinkManagerAction)> =
6237 adv_actions.into_iter().map(|a| ('i', a)).collect();
6238 let mut rounds = 0;
6239 let max_rounds = 50;
6240 let mut resource_received = false;
6241 let mut sender_completed = false;
6242
6243 while !pending.is_empty() && rounds < max_rounds {
6244 rounds += 1;
6245 let mut next: Vec<(char, LinkManagerAction)> = Vec::new();
6246
6247 for (source, action) in pending.drain(..) {
6248 if let LinkManagerAction::SendPacket { raw, .. } = action {
6249 let pkt = RawPacket::unpack(&raw).unwrap();
6250
6251 let target_actions = if source == 'i' {
6253 resp_mgr.handle_local_delivery(
6254 pkt.destination_hash,
6255 &raw,
6256 pkt.packet_hash,
6257 rns_core::transport::types::InterfaceId(0),
6258 &mut rng,
6259 )
6260 } else {
6261 init_mgr.handle_local_delivery(
6262 pkt.destination_hash,
6263 &raw,
6264 pkt.packet_hash,
6265 rns_core::transport::types::InterfaceId(0),
6266 &mut rng,
6267 )
6268 };
6269
6270 let target_source = if source == 'i' { 'r' } else { 'i' };
6271 for a in &target_actions {
6272 match a {
6273 LinkManagerAction::ResourceReceived { data, .. } => {
6274 assert_eq!(*data, original_data);
6275 resource_received = true;
6276 }
6277 LinkManagerAction::ResourceCompleted { .. } => {
6278 sender_completed = true;
6279 }
6280 _ => {}
6281 }
6282 }
6283 next.extend(target_actions.into_iter().map(|a| (target_source, a)));
6284 }
6285 }
6286 pending = next;
6287 }
6288
6289 assert!(
6290 resource_received,
6291 "Responder should receive resource data (rounds={})",
6292 rounds
6293 );
6294 assert!(
6295 sender_completed,
6296 "Sender should get completion proof (rounds={})",
6297 rounds
6298 );
6299 }
6300
6301 #[test]
6302 fn test_split_resource_advertisement_and_progress_entries() {
6303 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
6304 let mut rng = OsRng;
6305 let data = deterministic_bytes(constants::RESOURCE_MAX_EFFICIENT_SIZE + 1024);
6306
6307 let actions =
6308 init_mgr.send_resource_with_auto_compress(&link_id, &data, None, false, &mut rng);
6309 let adv = first_resource_advertisement(&init_mgr, &link_id, &actions);
6310
6311 assert!(adv.flags.split);
6312 assert_eq!(adv.segment_index, 1);
6313 assert_eq!(adv.total_segments, 2);
6314 assert_eq!(adv.data_size, data.len() as u64);
6315
6316 let managed = init_mgr.links.get(&link_id).unwrap();
6317 assert_eq!(managed.outgoing_splits.len(), 1);
6318 assert_eq!(
6319 managed
6320 .outgoing_resources
6321 .iter()
6322 .filter(|sender| sender.flags.split)
6323 .count(),
6324 2
6325 );
6326
6327 let entries = init_mgr.resource_entries();
6328 assert_eq!(entries.len(), 1);
6329 assert_eq!(entries[0].direction, "outgoing");
6330 assert!(entries[0].total_parts > managed.outgoing_resources[0].total_parts());
6331 assert_eq!(entries[0].transferred_parts, 0);
6332 }
6333
6334 #[test]
6335 fn test_split_resource_full_transfer_and_monotonic_progress() {
6336 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
6337 let mut rng = OsRng;
6338 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
6339
6340 let original_data = deterministic_bytes(constants::RESOURCE_MAX_EFFICIENT_SIZE + 2048);
6341 let initial_actions = init_mgr.send_resource_with_auto_compress(
6342 &link_id,
6343 &original_data,
6344 None,
6345 false,
6346 &mut rng,
6347 );
6348
6349 let (received_data, sender_completed, progress, failures, rounds) =
6350 drive_link_manager_packets(
6351 &mut init_mgr,
6352 &mut resp_mgr,
6353 initial_actions,
6354 'i',
6355 &mut rng,
6356 10_000,
6357 );
6358
6359 assert!(
6360 received_data.as_ref().is_some_and(|data| data == &original_data),
6361 "split transfer did not deliver payload in {rounds} rounds; sender_completed={sender_completed}; failures={failures:?}; last_progress={:?}; init_entries={:?}; resp_entries={:?}",
6362 progress.last(),
6363 init_mgr.resource_entries(),
6364 resp_mgr.resource_entries()
6365 );
6366 assert!(
6367 sender_completed,
6368 "sender did not complete in {rounds} rounds"
6369 );
6370 assert!(
6371 progress
6372 .iter()
6373 .any(|(_, received, total)| received == total),
6374 "expected final progress update"
6375 );
6376
6377 let mut init_last = 0;
6378 let mut resp_last = 0;
6379 for (side, received, total) in progress {
6380 assert!(received <= total);
6381 match side {
6382 'i' => {
6383 assert!(
6384 received >= init_last,
6385 "initiator progress regressed from {init_last} to {received}"
6386 );
6387 init_last = received;
6388 }
6389 'r' => {
6390 assert!(
6391 received >= resp_last,
6392 "responder progress regressed from {resp_last} to {received}"
6393 );
6394 resp_last = received;
6395 }
6396 _ => unreachable!(),
6397 }
6398 }
6399 }
6400
6401 #[test]
6402 fn test_split_resource_accept_app_queries_only_first_segment() {
6403 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
6404 let mut rng = OsRng;
6405 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptApp);
6406
6407 let original_data = deterministic_bytes(constants::RESOURCE_MAX_EFFICIENT_SIZE + 1024);
6408 let adv_actions = init_mgr.send_resource_with_auto_compress(
6409 &link_id,
6410 &original_data,
6411 None,
6412 false,
6413 &mut rng,
6414 );
6415 let adv_raw = extract_any_send_packet(&adv_actions);
6416 let adv_pkt = RawPacket::unpack(&adv_raw).unwrap();
6417 let query_actions = resp_mgr.handle_local_delivery(
6418 adv_pkt.destination_hash,
6419 &adv_raw,
6420 adv_pkt.packet_hash,
6421 rns_core::transport::types::InterfaceId(0),
6422 &mut rng,
6423 );
6424
6425 let queries: Vec<_> = query_actions
6426 .iter()
6427 .filter_map(|action| match action {
6428 LinkManagerAction::ResourceAcceptQuery { resource_hash, .. } => {
6429 Some(resource_hash.clone())
6430 }
6431 _ => None,
6432 })
6433 .collect();
6434 assert_eq!(queries.len(), 1);
6435
6436 let accept_actions = resp_mgr.accept_resource(&link_id, &queries[0], true, &mut rng);
6437 let (received_data, sender_completed, _progress, failures, rounds) =
6438 drive_link_manager_packets(
6439 &mut init_mgr,
6440 &mut resp_mgr,
6441 accept_actions,
6442 'r',
6443 &mut rng,
6444 10_000,
6445 );
6446
6447 assert!(
6448 failures.is_empty(),
6449 "split AcceptApp transfer failed: {failures:?}"
6450 );
6451 assert!(
6452 received_data
6453 .as_ref()
6454 .is_some_and(|data| data == &original_data),
6455 "split AcceptApp transfer did not deliver in {rounds} rounds"
6456 );
6457 assert!(sender_completed);
6458 }
6459
6460 #[test]
6461 fn test_resource_cancel_icl() {
6462 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
6463 let mut rng = OsRng;
6464
6465 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
6466
6467 let data = vec![0xAB; 2000];
6469 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
6470
6471 for action in &adv_actions {
6473 if let LinkManagerAction::SendPacket { raw, .. } = action {
6474 let pkt = RawPacket::unpack(raw).unwrap();
6475 resp_mgr.handle_local_delivery(
6476 pkt.destination_hash,
6477 raw,
6478 pkt.packet_hash,
6479 rns_core::transport::types::InterfaceId(0),
6480 &mut rng,
6481 );
6482 }
6483 }
6484
6485 assert!(!resp_mgr
6487 .links
6488 .get(&link_id)
6489 .unwrap()
6490 .incoming_resources
6491 .is_empty());
6492
6493 let icl_actions = resp_mgr.handle_resource_icl(&link_id);
6495
6496 let has_failed = icl_actions
6498 .iter()
6499 .any(|a| matches!(a, LinkManagerAction::ResourceFailed { .. }));
6500 assert!(has_failed, "ICL should produce ResourceFailed");
6501 }
6502
6503 #[test]
6504 fn test_resource_cancel_rcl() {
6505 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
6506 let mut rng = OsRng;
6507
6508 let data = vec![0xAB; 2000];
6510 init_mgr.send_resource(&link_id, &data, None, &mut rng);
6511
6512 assert!(!init_mgr
6514 .links
6515 .get(&link_id)
6516 .unwrap()
6517 .outgoing_resources
6518 .is_empty());
6519
6520 let rcl_actions = init_mgr.handle_resource_rcl(&link_id);
6522
6523 let has_failed = rcl_actions
6524 .iter()
6525 .any(|a| matches!(a, LinkManagerAction::ResourceFailed { .. }));
6526 assert!(has_failed, "RCL should produce ResourceFailed");
6527 }
6528
6529 #[test]
6530 fn test_cancel_all_resources_clears_active_transfers() {
6531 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
6532 let mut rng = OsRng;
6533
6534 let actions = init_mgr.send_resource(&link_id, b"resource body", None, &mut rng);
6535 assert!(!actions.is_empty());
6536 assert_eq!(init_mgr.resource_transfer_count(), 1);
6537
6538 let cancel_actions = init_mgr.cancel_all_resources(&mut rng);
6539
6540 assert_eq!(init_mgr.resource_transfer_count(), 0);
6541 assert!(cancel_actions.iter().any(|action| match action {
6542 LinkManagerAction::SendPacket { raw, .. } => RawPacket::unpack(raw)
6543 .map(|packet| packet.context == constants::CONTEXT_RESOURCE_ICL)
6544 .unwrap_or(false),
6545 _ => false,
6546 }));
6547 }
6548
6549 #[test]
6550 fn cancelling_stream_reports_one_terminal_failure() {
6551 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
6552 let mut rng = OsRng;
6553 let transfer_id = ResourceTransferId(42);
6554 let length = constants::RESOURCE_MAX_EFFICIENT_SIZE as u64 + 1;
6555 init_mgr.send_resource_stream(
6556 &link_id,
6557 transfer_id,
6558 Box::new(std::io::Cursor::new(vec![0x5a; length as usize])),
6559 length,
6560 None,
6561 false,
6562 &mut rng,
6563 );
6564
6565 let actions = init_mgr.cancel_all_resources(&mut rng);
6566 assert_eq!(
6567 actions
6568 .iter()
6569 .filter(|action| matches!(
6570 action,
6571 LinkManagerAction::ResourceStreamFailed {
6572 transfer_id: failed,
6573 error: ResourceTransferError::Cancelled,
6574 ..
6575 } if *failed == transfer_id
6576 ))
6577 .count(),
6578 1
6579 );
6580 assert_eq!(init_mgr.resource_transfer_count(), 0);
6581 }
6582
6583 #[test]
6584 fn receiver_cancel_sends_rcl_while_active_and_closed_cleanup_is_local_only() {
6585 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
6586 let mut rng = OsRng;
6587 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptApp);
6588
6589 let advertisement = init_mgr.send_resource(&link_id, b"incoming body", None, &mut rng);
6590 let raw = extract_any_send_packet(&advertisement);
6591 let packet = RawPacket::unpack(&raw).unwrap();
6592 resp_mgr.handle_local_delivery(
6593 packet.destination_hash,
6594 &raw,
6595 packet.packet_hash,
6596 rns_core::transport::types::InterfaceId(0),
6597 &mut rng,
6598 );
6599 assert_eq!(resp_mgr.resource_transfer_count(), 1);
6600 let active_cancel = resp_mgr.cancel_all_resources(&mut rng);
6601 assert_eq!(resp_mgr.resource_transfer_count(), 0);
6602 assert!(active_cancel.iter().any(|action| match action {
6603 LinkManagerAction::SendPacket { raw, .. } => RawPacket::unpack(raw)
6604 .map(|packet| packet.context == constants::CONTEXT_RESOURCE_RCL)
6605 .unwrap_or(false),
6606 _ => false,
6607 }));
6608
6609 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
6610 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptApp);
6611 let advertisement = init_mgr.send_resource(&link_id, b"second body", None, &mut rng);
6612 let raw = extract_any_send_packet(&advertisement);
6613 let packet = RawPacket::unpack(&raw).unwrap();
6614 resp_mgr.handle_local_delivery(
6615 packet.destination_hash,
6616 &raw,
6617 packet.packet_hash,
6618 rns_core::transport::types::InterfaceId(0),
6619 &mut rng,
6620 );
6621 resp_mgr
6622 .links
6623 .get_mut(&link_id)
6624 .unwrap()
6625 .engine
6626 .handle_teardown();
6627 let closed_cancel = resp_mgr.cancel_all_resources(&mut rng);
6628 assert_eq!(resp_mgr.resource_transfer_count(), 0);
6629 assert!(!closed_cancel
6630 .iter()
6631 .any(|action| matches!(action, LinkManagerAction::SendPacket { .. })));
6632 }
6633
6634 #[test]
6635 fn stale_sender_cancel_cleans_up_without_icl() {
6636 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
6637 let mut rng = OsRng;
6638 init_mgr.send_resource(&link_id, b"stale transfer", None, &mut rng);
6639 init_mgr
6640 .links
6641 .get_mut(&link_id)
6642 .unwrap()
6643 .engine
6644 .tick(time::now() + 100_000.0);
6645 assert_eq!(init_mgr.link_state(&link_id), Some(LinkState::Stale));
6646
6647 let actions = init_mgr.cancel_all_resources(&mut rng);
6648 assert_eq!(init_mgr.resource_transfer_count(), 0);
6649 assert!(!actions
6650 .iter()
6651 .any(|action| matches!(action, LinkManagerAction::SendPacket { .. })));
6652 }
6653
6654 #[test]
6655 fn test_resource_tick_cleans_up() {
6656 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
6657 let mut rng = OsRng;
6658
6659 let data = vec![0xAB; 100];
6660 init_mgr.send_resource(&link_id, &data, None, &mut rng);
6661
6662 assert!(!init_mgr
6663 .links
6664 .get(&link_id)
6665 .unwrap()
6666 .outgoing_resources
6667 .is_empty());
6668
6669 init_mgr.handle_resource_rcl(&link_id);
6671
6672 init_mgr.tick(&mut rng);
6674
6675 assert!(
6676 init_mgr
6677 .links
6678 .get(&link_id)
6679 .unwrap()
6680 .outgoing_resources
6681 .is_empty(),
6682 "Tick should clean up completed/failed outgoing resources"
6683 );
6684 }
6685
6686 #[test]
6687 fn test_build_link_packet() {
6688 let (init_mgr, _resp_mgr, link_id) = setup_active_link();
6689
6690 let actions =
6691 init_mgr.build_link_packet(&link_id, constants::CONTEXT_RESOURCE, b"test data");
6692 assert_eq!(actions.len(), 1);
6693 if let LinkManagerAction::SendPacket { raw, dest_type, .. } = &actions[0] {
6694 let pkt = RawPacket::unpack(raw).unwrap();
6695 assert_eq!(pkt.context, constants::CONTEXT_RESOURCE);
6696 assert_eq!(*dest_type, constants::DESTINATION_LINK);
6697 } else {
6698 panic!("Expected SendPacket");
6699 }
6700 }
6701
6702 #[test]
6703 fn link_traffic_accounting_uses_ciphertext_data_once() {
6704 let (mut initiator, mut responder, link_id) = setup_active_link();
6705 let before_tx = initiator.links[&link_id].engine.tx_packets();
6706 let before_tx_bytes = initiator.links[&link_id].engine.tx_bytes();
6707 let before_rx = responder.links[&link_id].engine.rx_packets();
6708 let before_rx_bytes = responder.links[&link_id].engine.rx_bytes();
6709 let actions = initiator.send_on_link(
6710 &link_id,
6711 b"short plaintext",
6712 constants::CONTEXT_NONE,
6713 &mut OsRng,
6714 );
6715 let raw = actions
6716 .iter()
6717 .find_map(|action| match action {
6718 LinkManagerAction::SendPacket { raw, .. } => Some(raw.clone()),
6719 _ => None,
6720 })
6721 .unwrap();
6722 let packet = RawPacket::unpack(&raw).unwrap();
6723 assert!(packet.data.len() > b"short plaintext".len());
6724
6725 initiator.record_outbound_packet(&packet);
6726 assert_eq!(initiator.links[&link_id].engine.tx_packets(), before_tx + 1);
6727 assert_eq!(
6728 initiator.links[&link_id].engine.tx_bytes(),
6729 before_tx_bytes + packet.data.len() as u64
6730 );
6731
6732 responder.handle_local_delivery(
6733 link_id,
6734 &raw,
6735 packet.packet_hash,
6736 rns_core::transport::types::InterfaceId(1),
6737 &mut OsRng,
6738 );
6739 assert_eq!(responder.links[&link_id].engine.rx_packets(), before_rx + 1);
6740 assert_eq!(
6741 responder.links[&link_id].engine.rx_bytes(),
6742 before_rx_bytes + packet.data.len() as u64
6743 );
6744 }
6745
6746 #[test]
6747 fn test_build_link_packet_returns_empty_when_mtu_too_small() {
6748 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
6749 init_mgr.set_link_mtu(&link_id, 84);
6750
6751 let actions =
6752 init_mgr.build_link_packet(&link_id, constants::CONTEXT_RESOURCE, &[0xAA; 200]);
6753 assert!(actions.is_empty(), "oversized packet should not be built");
6754 }
6755
6756 #[test]
6757 fn test_process_resource_actions_encrypted_variants_drop_on_encrypt_failure() {
6758 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
6759 let mut rng = OsRng;
6760 init_mgr
6761 .links
6762 .get_mut(&link_id)
6763 .unwrap()
6764 .engine
6765 .clear_session_for_testing();
6766
6767 let cases = vec![
6768 ResourceAction::SendAdvertisement(vec![1, 2, 3]),
6769 ResourceAction::SendRequest(vec![4, 5, 6]),
6770 ResourceAction::SendHmu(vec![7, 8, 9]),
6771 ResourceAction::SendCancelInitiator(vec![13, 14, 15]),
6772 ResourceAction::SendCancelReceiver(vec![16, 17, 18]),
6773 ];
6774
6775 for action in cases {
6776 let out = init_mgr.process_resource_actions(&link_id, vec![action], &mut rng);
6777 assert!(
6778 out.is_empty(),
6779 "encrypt failure should suppress packet emission"
6780 );
6781 }
6782 }
6783
6784 #[test]
6785 fn test_resource_proof_is_raw_proof_packet() {
6786 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
6787 let mut rng = OsRng;
6788 let proof = vec![0x22; 64];
6789
6790 let out = init_mgr.process_resource_actions(
6791 &link_id,
6792 vec![ResourceAction::SendProof(proof.clone())],
6793 &mut rng,
6794 );
6795 let raw = extract_any_send_packet(&out);
6796 let pkt = RawPacket::unpack(&raw).unwrap();
6797
6798 assert_eq!(pkt.flags.packet_type, constants::PACKET_TYPE_PROOF);
6799 assert_eq!(pkt.context, constants::CONTEXT_RESOURCE_PRF);
6800 assert_eq!(pkt.data, proof);
6801 }
6802
6803 #[test]
6808 fn test_channel_message_delivery() {
6809 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
6810 let mut rng = OsRng;
6811
6812 let chan_actions = init_mgr
6814 .send_channel_message(&link_id, 42, b"channel data", &mut rng)
6815 .expect("active link channel send should succeed");
6816 assert!(!chan_actions.is_empty());
6817
6818 let mut got_channel_msg = false;
6820 for action in &chan_actions {
6821 if let LinkManagerAction::SendPacket { raw, .. } = action {
6822 let pkt = RawPacket::unpack(raw).unwrap();
6823 let resp_actions = resp_mgr.handle_local_delivery(
6824 pkt.destination_hash,
6825 raw,
6826 pkt.packet_hash,
6827 rns_core::transport::types::InterfaceId(0),
6828 &mut rng,
6829 );
6830 for a in &resp_actions {
6831 if let LinkManagerAction::ChannelMessageReceived {
6832 msgtype, payload, ..
6833 } = a
6834 {
6835 assert_eq!(*msgtype, 42);
6836 assert_eq!(*payload, b"channel data");
6837 got_channel_msg = true;
6838 }
6839 }
6840 }
6841 }
6842 assert!(got_channel_msg, "Responder should receive channel message");
6843 }
6844
6845 #[test]
6846 fn test_channel_send_drops_packet_when_encrypt_fails() {
6847 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
6848 let mut rng = OsRng;
6849 init_mgr
6850 .links
6851 .get_mut(&link_id)
6852 .unwrap()
6853 .engine
6854 .clear_session_for_testing();
6855
6856 let actions = init_mgr
6857 .send_channel_message(&link_id, 42, b"channel data", &mut rng)
6858 .expect("channel should still accept the message locally");
6859
6860 assert!(
6861 actions.is_empty(),
6862 "encrypt failure should suppress channel packet"
6863 );
6864 assert!(
6865 init_mgr
6866 .links
6867 .get(&link_id)
6868 .unwrap()
6869 .pending_channel_packets
6870 .is_empty(),
6871 "failed packet encryption must not track a pending channel proof"
6872 );
6873 }
6874
6875 #[test]
6876 fn test_channel_proof_reopens_send_window() {
6877 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
6878 let mut rng = OsRng;
6879
6880 init_mgr
6881 .send_channel_message(&link_id, 42, b"first", &mut rng)
6882 .expect("first send should succeed");
6883 init_mgr
6884 .send_channel_message(&link_id, 42, b"second", &mut rng)
6885 .expect("second send should succeed");
6886
6887 let err = init_mgr
6888 .send_channel_message(&link_id, 42, b"third", &mut rng)
6889 .expect_err("third send should hit the initial channel window");
6890 assert_eq!(err, "Channel is not ready to send");
6891
6892 let queued_packets = init_mgr
6893 .links
6894 .get(&link_id)
6895 .unwrap()
6896 .pending_channel_packets
6897 .clone();
6898 assert_eq!(queued_packets.len(), 2);
6899 for tracked_hash in queued_packets.keys().take(1) {
6900 let mut proof_data = Vec::with_capacity(96);
6901 proof_data.extend_from_slice(tracked_hash);
6902 proof_data.extend_from_slice(&[0x11; 64]);
6903 let flags = PacketFlags {
6904 header_type: constants::HEADER_1,
6905 context_flag: constants::FLAG_UNSET,
6906 transport_type: constants::TRANSPORT_BROADCAST,
6907 destination_type: constants::DESTINATION_LINK,
6908 packet_type: constants::PACKET_TYPE_PROOF,
6909 };
6910 let proof = RawPacket::pack(
6911 flags,
6912 0,
6913 &link_id,
6914 None,
6915 constants::CONTEXT_NONE,
6916 &proof_data,
6917 )
6918 .expect("proof packet should pack");
6919 let ack_actions = init_mgr.handle_local_delivery(
6920 link_id,
6921 &proof.raw,
6922 proof.packet_hash,
6923 rns_core::transport::types::InterfaceId(0),
6924 &mut rng,
6925 );
6926 assert!(
6927 ack_actions.is_empty(),
6928 "proof delivery should only update channel state"
6929 );
6930 }
6931
6932 init_mgr
6933 .send_channel_message(&link_id, 42, b"third", &mut rng)
6934 .expect("proof should free one channel slot");
6935 }
6936
6937 #[test]
6938 fn test_generic_link_data_delivery() {
6939 let (init_mgr, mut resp_mgr, link_id) = setup_active_link();
6940 let mut rng = OsRng;
6941
6942 let actions = init_mgr.send_on_link(&link_id, b"raw stuff", 0x42, &mut rng);
6944 assert_eq!(actions.len(), 1);
6945
6946 let raw = extract_any_send_packet(&actions);
6948 let pkt = RawPacket::unpack(&raw).unwrap();
6949 let resp_actions = resp_mgr.handle_local_delivery(
6950 pkt.destination_hash,
6951 &raw,
6952 pkt.packet_hash,
6953 rns_core::transport::types::InterfaceId(0),
6954 &mut rng,
6955 );
6956
6957 let has_data = resp_actions
6958 .iter()
6959 .any(|a| matches!(a, LinkManagerAction::LinkDataReceived { context: 0x42, .. }));
6960 assert!(
6961 has_data,
6962 "Responder should receive LinkDataReceived for unknown context"
6963 );
6964 }
6965
6966 #[test]
6967 fn test_invalid_encrypted_contexts_are_ignored() {
6968 let (_init_mgr, mut resp_mgr, link_id) = setup_active_link();
6969 let mut rng = OsRng;
6970 let contexts = [
6971 constants::CONTEXT_CHANNEL,
6972 constants::CONTEXT_REQUEST,
6973 constants::CONTEXT_RESPONSE,
6974 constants::CONTEXT_RESOURCE_ADV,
6975 constants::CONTEXT_RESOURCE_REQ,
6976 constants::CONTEXT_RESOURCE_HMU,
6977 constants::CONTEXT_RESOURCE_PRF,
6978 0x42,
6979 ];
6980
6981 for context in contexts {
6982 let flags = PacketFlags {
6983 header_type: constants::HEADER_1,
6984 context_flag: constants::FLAG_UNSET,
6985 transport_type: constants::TRANSPORT_BROADCAST,
6986 destination_type: constants::DESTINATION_LINK,
6987 packet_type: constants::PACKET_TYPE_DATA,
6988 };
6989 let pkt = RawPacket::pack(flags, 0, &link_id, None, context, b"invalid-ciphertext")
6990 .expect("test packet should pack");
6991 let actions = resp_mgr.handle_local_delivery(
6992 pkt.destination_hash,
6993 &pkt.raw,
6994 pkt.packet_hash,
6995 rns_core::transport::types::InterfaceId(0),
6996 &mut rng,
6997 );
6998 assert!(
6999 actions.is_empty(),
7000 "invalid ciphertext for context {context:#x} should be ignored"
7001 );
7002 }
7003 }
7004
7005 #[test]
7006 fn test_resource_part_without_matching_receiver_is_ignored() {
7007 let (_init_mgr, mut resp_mgr, link_id) = setup_active_link();
7008 let mut rng = OsRng;
7009 let flags = PacketFlags {
7010 header_type: constants::HEADER_1,
7011 context_flag: constants::FLAG_UNSET,
7012 transport_type: constants::TRANSPORT_BROADCAST,
7013 destination_type: constants::DESTINATION_LINK,
7014 packet_type: constants::PACKET_TYPE_DATA,
7015 };
7016 let pkt = RawPacket::pack(
7017 flags,
7018 0,
7019 &link_id,
7020 None,
7021 constants::CONTEXT_RESOURCE,
7022 b"orphan-part",
7023 )
7024 .expect("test packet should pack");
7025
7026 let actions = resp_mgr.handle_local_delivery(
7027 pkt.destination_hash,
7028 &pkt.raw,
7029 pkt.packet_hash,
7030 rns_core::transport::types::InterfaceId(0),
7031 &mut rng,
7032 );
7033
7034 assert!(actions.is_empty(), "orphan resource part should be ignored");
7035 }
7036
7037 #[test]
7038 fn test_response_delivery() {
7039 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
7040 let mut rng = OsRng;
7041
7042 resp_mgr.register_request_handler("/echo", None, |_link_id, _path, data, _remote| {
7044 Some(data.to_vec())
7045 });
7046
7047 let req_actions = init_mgr.send_request(&link_id, "/echo", b"\xc0", &mut rng); assert!(!req_actions.is_empty());
7050
7051 let req_raw = extract_any_send_packet(&req_actions);
7053 let req_pkt = RawPacket::unpack(&req_raw).unwrap();
7054 let resp_actions = resp_mgr.handle_local_delivery(
7055 req_pkt.destination_hash,
7056 &req_raw,
7057 req_pkt.packet_hash,
7058 rns_core::transport::types::InterfaceId(0),
7059 &mut rng,
7060 );
7061 let has_resp_send = resp_actions
7062 .iter()
7063 .any(|a| matches!(a, LinkManagerAction::SendPacket { .. }));
7064 assert!(has_resp_send, "Handler should produce response");
7065
7066 let resp_raw = extract_any_send_packet(&resp_actions);
7068 let resp_pkt = RawPacket::unpack(&resp_raw).unwrap();
7069 let init_actions = init_mgr.handle_local_delivery(
7070 resp_pkt.destination_hash,
7071 &resp_raw,
7072 resp_pkt.packet_hash,
7073 rns_core::transport::types::InterfaceId(0),
7074 &mut rng,
7075 );
7076
7077 let has_response_received = init_actions
7078 .iter()
7079 .any(|a| matches!(a, LinkManagerAction::ResponseReceived { .. }));
7080 assert!(
7081 has_response_received,
7082 "Initiator should receive ResponseReceived"
7083 );
7084 }
7085
7086 #[test]
7087 fn deferred_request_response_round_trip() {
7088 use std::sync::{Arc, Mutex};
7089
7090 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
7091 let mut rng = OsRng;
7092 let deferred = Arc::new(Mutex::new(None));
7093 resp_mgr.register_deferred_request_handler("/deferred", None, {
7094 let deferred = Arc::clone(&deferred);
7095 move |received_link, _, request_id, data, _| {
7096 *deferred.lock().unwrap() = Some((received_link, request_id, data.to_vec()));
7097 }
7098 });
7099
7100 let request_actions = init_mgr.send_request(&link_id, "/deferred", b"\xa5hello", &mut rng);
7101 let request_raw = extract_any_send_packet(&request_actions);
7102 let request_packet = RawPacket::unpack(&request_raw).unwrap();
7103 let immediate = resp_mgr.handle_local_delivery(
7104 request_packet.destination_hash,
7105 &request_raw,
7106 request_packet.packet_hash,
7107 rns_core::transport::types::InterfaceId(0),
7108 &mut rng,
7109 );
7110 assert!(
7111 immediate
7112 .iter()
7113 .all(|action| !matches!(action, LinkManagerAction::SendPacket { .. })),
7114 "deferred handlers must not send an immediate response"
7115 );
7116
7117 let (received_link, request_id, data) = deferred.lock().unwrap().take().unwrap();
7118 assert_eq!(received_link, link_id);
7119 assert_eq!(data, b"\xa5hello");
7120 let response_actions =
7121 resp_mgr.send_deferred_response(&link_id, &request_id, b"\xa2OK", &mut rng);
7122 let response_raw = extract_any_send_packet(&response_actions);
7123 let response_packet = RawPacket::unpack(&response_raw).unwrap();
7124 let delivered = init_mgr.handle_local_delivery(
7125 response_packet.destination_hash,
7126 &response_raw,
7127 response_packet.packet_hash,
7128 rns_core::transport::types::InterfaceId(0),
7129 &mut rng,
7130 );
7131 assert!(delivered.iter().any(|action| matches!(
7132 action,
7133 LinkManagerAction::ResponseReceived { data, .. } if data == b"\xa2OK"
7134 )));
7135 }
7136
7137 #[test]
7138 fn test_large_response_uses_resource_fallback() {
7139 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
7140 let mut rng = OsRng;
7141
7142 let large_payload: Vec<u8> = (0..5000u32).map(|i| (i & 0xFF) as u8).collect();
7145 resp_mgr.register_request_handler("/large", None, {
7146 let large_payload = large_payload.clone();
7147 move |_link_id, _path, _data, _remote| Some(large_payload.clone())
7148 });
7149
7150 let req_actions = init_mgr.send_request(&link_id, "/large", b"\xc0", &mut rng);
7152 assert!(!req_actions.is_empty());
7153
7154 let req_raw = extract_any_send_packet(&req_actions);
7156 let req_pkt = RawPacket::unpack(&req_raw).unwrap();
7157 let resp_actions = resp_mgr.handle_local_delivery(
7158 req_pkt.destination_hash,
7159 &req_raw,
7160 req_pkt.packet_hash,
7161 rns_core::transport::types::InterfaceId(0),
7162 &mut rng,
7163 );
7164
7165 let mut has_resource_adv = false;
7166 let mut has_direct_response = false;
7167 for action in &resp_actions {
7168 if let LinkManagerAction::SendPacket { raw, .. } = action {
7169 let pkt = RawPacket::unpack(raw).unwrap();
7170 if pkt.context == constants::CONTEXT_RESOURCE_ADV {
7171 has_resource_adv = true;
7172 }
7173 if pkt.context == constants::CONTEXT_RESPONSE {
7174 has_direct_response = true;
7175 }
7176 }
7177 }
7178
7179 assert!(
7180 has_resource_adv,
7181 "Large response should advertise a response resource"
7182 );
7183 assert!(
7184 !has_direct_response,
7185 "Large response should not use direct CONTEXT_RESPONSE packet"
7186 );
7187 }
7188
7189 #[test]
7190 fn test_send_management_response_without_session_key_uses_resource_fallback_path() {
7191 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
7192 let mut rng = OsRng;
7193 init_mgr
7194 .links
7195 .get_mut(&link_id)
7196 .unwrap()
7197 .engine
7198 .clear_session_for_testing();
7199
7200 let large_response: Vec<u8> = (0..5000u32).map(|i| (i & 0xFF) as u8).collect();
7201 let actions =
7202 init_mgr.send_management_response(&link_id, &[0x11; 16], &large_response, &mut rng);
7203
7204 assert!(
7205 actions.is_empty(),
7206 "without a session key, no response packets should be emitted"
7207 );
7208 assert_eq!(
7209 init_mgr
7210 .links
7211 .get(&link_id)
7212 .map(|managed| managed.outgoing_resources.len()),
7213 Some(1)
7214 );
7215 }
7216
7217 #[test]
7218 fn test_send_channel_message_on_no_channel() {
7219 let mut mgr = LinkManager::new();
7220 let mut rng = OsRng;
7221 let dummy_sig = [0xAA; 32];
7222 let (link_id, _) =
7223 mgr.create_link(&[0x11; 16], &dummy_sig, 1, constants::MTU as u32, &mut rng);
7224
7225 let err = mgr
7227 .send_channel_message(&link_id, 1, b"test", &mut rng)
7228 .expect_err("pending link should reject channel send");
7229 assert_eq!(err, "link has no active channel");
7230 }
7231
7232 #[test]
7233 fn test_send_on_link_requires_active() {
7234 let mut mgr = LinkManager::new();
7235 let mut rng = OsRng;
7236 let dummy_sig = [0xAA; 32];
7237 let (link_id, _) =
7238 mgr.create_link(&[0x11; 16], &dummy_sig, 1, constants::MTU as u32, &mut rng);
7239
7240 let actions = mgr.send_on_link(&link_id, b"test", constants::CONTEXT_NONE, &mut rng);
7241 assert!(actions.is_empty(), "Cannot send on pending link");
7242 }
7243
7244 #[test]
7245 fn test_send_on_link_unknown_link() {
7246 let mgr = LinkManager::new();
7247 let mut rng = OsRng;
7248
7249 let actions = mgr.send_on_link(&[0xFF; 16], b"test", constants::CONTEXT_NONE, &mut rng);
7250 assert!(actions.is_empty());
7251 }
7252
7253 #[test]
7254 fn test_resource_full_transfer_large() {
7255 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
7256 let mut rng = OsRng;
7257
7258 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
7259
7260 let original_data: Vec<u8> = (0..2000u32)
7262 .map(|i| {
7263 let pos = i as usize;
7264 (pos ^ (pos >> 8) ^ (pos >> 16)) as u8
7265 })
7266 .collect();
7267
7268 let adv_actions = init_mgr.send_resource(&link_id, &original_data, None, &mut rng);
7269
7270 let mut pending: Vec<(char, LinkManagerAction)> =
7271 adv_actions.into_iter().map(|a| ('i', a)).collect();
7272 let mut rounds = 0;
7273 let max_rounds = 200;
7274 let mut resource_received = false;
7275 let mut sender_completed = false;
7276
7277 while !pending.is_empty() && rounds < max_rounds {
7278 rounds += 1;
7279 let mut next: Vec<(char, LinkManagerAction)> = Vec::new();
7280
7281 for (source, action) in pending.drain(..) {
7282 if let LinkManagerAction::SendPacket { raw, .. } = action {
7283 let pkt = match RawPacket::unpack(&raw) {
7284 Ok(p) => p,
7285 Err(_) => continue,
7286 };
7287
7288 let target_actions = if source == 'i' {
7289 resp_mgr.handle_local_delivery(
7290 pkt.destination_hash,
7291 &raw,
7292 pkt.packet_hash,
7293 rns_core::transport::types::InterfaceId(0),
7294 &mut rng,
7295 )
7296 } else {
7297 init_mgr.handle_local_delivery(
7298 pkt.destination_hash,
7299 &raw,
7300 pkt.packet_hash,
7301 rns_core::transport::types::InterfaceId(0),
7302 &mut rng,
7303 )
7304 };
7305
7306 let target_source = if source == 'i' { 'r' } else { 'i' };
7307 for a in &target_actions {
7308 match a {
7309 LinkManagerAction::ResourceReceived { data, .. } => {
7310 assert_eq!(*data, original_data);
7311 resource_received = true;
7312 }
7313 LinkManagerAction::ResourceCompleted { .. } => {
7314 sender_completed = true;
7315 }
7316 _ => {}
7317 }
7318 }
7319 next.extend(target_actions.into_iter().map(|a| (target_source, a)));
7320 }
7321 }
7322 pending = next;
7323 }
7324
7325 assert!(
7326 resource_received,
7327 "Should receive large resource (rounds={})",
7328 rounds
7329 );
7330 assert!(
7331 sender_completed,
7332 "Sender should complete (rounds={})",
7333 rounds
7334 );
7335 }
7336
7337 #[test]
7338 fn test_resource_receiver_stores_original_advertisement_plaintext() {
7339 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
7340 let mut rng = OsRng;
7341
7342 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
7343
7344 let data = vec![0x41; 256];
7345 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
7346
7347 let adv_raw = adv_actions
7348 .iter()
7349 .find_map(|action| match action {
7350 LinkManagerAction::SendPacket { raw, .. } => {
7351 let pkt = RawPacket::unpack(raw).ok()?;
7352 (pkt.context == constants::CONTEXT_RESOURCE_ADV).then_some(raw.clone())
7353 }
7354 _ => None,
7355 })
7356 .expect("sender should emit a resource advertisement");
7357
7358 let adv_pkt = RawPacket::unpack(&adv_raw).unwrap();
7359 let adv_plaintext = resp_mgr
7360 .links
7361 .get(&link_id)
7362 .unwrap()
7363 .engine
7364 .decrypt(&adv_pkt.data)
7365 .unwrap();
7366
7367 let _resp_actions = resp_mgr.handle_local_delivery(
7368 adv_pkt.destination_hash,
7369 &adv_raw,
7370 adv_pkt.packet_hash,
7371 rns_core::transport::types::InterfaceId(0),
7372 &mut rng,
7373 );
7374
7375 let receiver = resp_mgr
7376 .links
7377 .get(&link_id)
7378 .and_then(|managed| managed.incoming_resources.first())
7379 .expect("advertisement should create an incoming receiver");
7380 assert_eq!(receiver.advertisement_packet, adv_plaintext);
7381 assert_eq!(
7382 receiver.max_decompressed_size,
7383 constants::RESOURCE_AUTO_COMPRESS_MAX_SIZE
7384 );
7385 }
7386
7387 #[test]
7388 fn test_corrupt_compressed_resource_rejects_and_tears_down_link() {
7389 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
7390 let mut rng = OsRng;
7391
7392 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
7393
7394 let data = vec![b'A'; 4096];
7395 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
7396
7397 let mut request_actions = Vec::new();
7398 for action in &adv_actions {
7399 let LinkManagerAction::SendPacket { raw, .. } = action else {
7400 continue;
7401 };
7402 let pkt = RawPacket::unpack(raw).unwrap();
7403 let actions = resp_mgr.handle_local_delivery(
7404 pkt.destination_hash,
7405 raw,
7406 pkt.packet_hash,
7407 rns_core::transport::types::InterfaceId(0),
7408 &mut rng,
7409 );
7410 request_actions.extend(actions);
7411 }
7412
7413 {
7414 let receiver = resp_mgr
7415 .links
7416 .get_mut(&link_id)
7417 .and_then(|managed| managed.incoming_resources.first_mut())
7418 .expect("receiver should exist after advertisement");
7419 assert!(receiver.flags.compressed, "test data should be compressed");
7420 receiver.max_decompressed_size = 64;
7421 }
7422
7423 let mut responder_actions = Vec::new();
7424 for action in request_actions {
7425 let LinkManagerAction::SendPacket { raw, .. } = action else {
7426 continue;
7427 };
7428 let pkt = RawPacket::unpack(&raw).unwrap();
7429 let actions = init_mgr.handle_local_delivery(
7430 pkt.destination_hash,
7431 &raw,
7432 pkt.packet_hash,
7433 rns_core::transport::types::InterfaceId(0),
7434 &mut rng,
7435 );
7436
7437 for action in actions {
7438 let LinkManagerAction::SendPacket { raw, .. } = &action else {
7439 continue;
7440 };
7441 let pkt = RawPacket::unpack(raw).unwrap();
7442 let delivered = resp_mgr.handle_local_delivery(
7443 pkt.destination_hash,
7444 raw,
7445 pkt.packet_hash,
7446 rns_core::transport::types::InterfaceId(0),
7447 &mut rng,
7448 );
7449 responder_actions.extend(delivered);
7450 }
7451 }
7452
7453 assert!(
7454 responder_actions.iter().any(|action| matches!(
7455 action,
7456 LinkManagerAction::ResourceFailed { error, .. }
7457 if error == "Resource too large"
7458 )),
7459 "corrupt oversized resource should fail with TooLarge"
7460 );
7461 assert!(
7462 responder_actions.iter().any(|action| matches!(
7463 action,
7464 LinkManagerAction::LinkClosed { link_id: closed_id, .. } if *closed_id == link_id
7465 )),
7466 "corrupt oversized resource should tear down the link"
7467 );
7468 assert!(
7469 responder_actions.iter().any(|action| match action {
7470 LinkManagerAction::SendPacket { raw, .. } => RawPacket::unpack(raw)
7471 .map(|pkt| pkt.context == constants::CONTEXT_RESOURCE_RCL)
7472 .unwrap_or(false),
7473 _ => false,
7474 }),
7475 "corrupt oversized resource should send a receiver cancel/reject packet"
7476 );
7477 assert_eq!(
7478 resp_mgr
7479 .links
7480 .get(&link_id)
7481 .map(|managed| managed.engine.state()),
7482 Some(LinkState::Closed)
7483 );
7484 }
7485
7486 #[test]
7487 fn test_resource_hmu_timeout_extension_in_link_manager_flow() {
7488 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
7489 let mut rng = OsRng;
7490
7491 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
7492
7493 let mut state = 0x1234_5678u32;
7496 let data: Vec<u8> = (0..50000)
7497 .map(|_| {
7498 state = state.wrapping_mul(1664525).wrapping_add(1013904223);
7499 (state >> 16) as u8
7500 })
7501 .collect();
7502 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
7503 let mut pending: Vec<(char, LinkManagerAction)> =
7504 adv_actions.into_iter().map(|a| ('i', a)).collect();
7505
7506 let mut rounds = 0;
7507
7508 while rounds < 300 {
7511 rounds += 1;
7512 let mut next: Vec<(char, LinkManagerAction)> = Vec::new();
7513
7514 for (source, action) in pending.drain(..) {
7515 let LinkManagerAction::SendPacket { raw, .. } = action else {
7516 continue;
7517 };
7518
7519 let pkt = match RawPacket::unpack(&raw) {
7520 Ok(p) => p,
7521 Err(_) => continue,
7522 };
7523
7524 let target_actions = if source == 'i' {
7525 resp_mgr.handle_local_delivery(
7526 pkt.destination_hash,
7527 &raw,
7528 pkt.packet_hash,
7529 rns_core::transport::types::InterfaceId(0),
7530 &mut rng,
7531 )
7532 } else {
7533 init_mgr.handle_local_delivery(
7534 pkt.destination_hash,
7535 &raw,
7536 pkt.packet_hash,
7537 rns_core::transport::types::InterfaceId(0),
7538 &mut rng,
7539 )
7540 };
7541
7542 let target_source = if source == 'i' { 'r' } else { 'i' };
7543 next.extend(target_actions.into_iter().map(|a| (target_source, a)));
7544 }
7545
7546 if resp_mgr
7547 .links
7548 .get(&link_id)
7549 .and_then(|managed| managed.incoming_resources.first())
7550 .is_some_and(|receiver| receiver.waiting_for_hmu)
7551 {
7552 break;
7553 }
7554
7555 pending = next;
7556 }
7557
7558 assert!(
7559 resp_mgr
7560 .links
7561 .get(&link_id)
7562 .and_then(|managed| managed.incoming_resources.first())
7563 .is_some_and(|receiver| receiver.waiting_for_hmu),
7564 "expected receiver to reach a live HMU wait state"
7565 );
7566
7567 let prime_actions = {
7570 let managed = resp_mgr.links.get_mut(&link_id).unwrap();
7571 let receiver = managed.incoming_resources.first_mut().unwrap();
7572 let decrypt_fn = |ciphertext: &[u8]| -> Result<Vec<u8>, ()> {
7573 managed.engine.decrypt(ciphertext).map_err(|_| ())
7574 };
7575 receiver.tick(
7576 receiver.last_activity + 0.0001,
7577 &decrypt_fn,
7578 &Bzip2Compressor,
7579 )
7580 };
7581 assert!(
7582 !prime_actions
7583 .iter()
7584 .any(|a| matches!(a, ResourceAction::SendRequest(_))),
7585 "fresh HMU wait state should not immediately emit a retry request"
7586 );
7587
7588 let (late_delta, retries_before) = {
7589 let managed = resp_mgr
7590 .links
7591 .get_mut(&link_id)
7592 .expect("receiver link should still exist");
7593 let receiver = managed
7594 .incoming_resources
7595 .first_mut()
7596 .expect("receiver should have an active incoming resource");
7597
7598 assert!(
7599 receiver.waiting_for_hmu,
7600 "receiver should be waiting for HMU"
7601 );
7602
7603 let eifr = receiver.eifr.unwrap_or_else(|| {
7604 (constants::RESOURCE_SDU as f64 * 8.0) / receiver.rtt.unwrap_or(0.5)
7605 });
7606 let expected_tof = if receiver.outstanding_parts > 0 {
7607 (receiver.outstanding_parts as f64 * constants::RESOURCE_SDU as f64 * 8.0) / eifr
7608 } else {
7609 (3.0 * constants::RESOURCE_SDU as f64) / eifr
7610 };
7611 let expected_hmu_wait =
7612 (constants::RESOURCE_SDU as f64 * 8.0 * constants::RESOURCE_HMU_WAIT_FACTOR) / eifr;
7613 let old_delta = constants::RESOURCE_PART_TIMEOUT_FACTOR_AFTER_RTT * expected_tof
7614 + constants::RESOURCE_RETRY_GRACE_TIME;
7615 (
7616 old_delta + expected_hmu_wait + expected_hmu_wait.max(1.0),
7617 receiver.retries_left,
7618 )
7619 };
7620 {
7621 let managed = resp_mgr.links.get(&link_id).unwrap();
7622 let receiver = managed.incoming_resources.first().unwrap();
7623 assert_eq!(receiver.retries_left, retries_before);
7624 assert!(
7625 receiver.eifr.is_some(),
7626 "receiver tick should have populated EIFR"
7627 );
7628 }
7629
7630 let late_resource_actions = {
7631 let managed = resp_mgr.links.get_mut(&link_id).unwrap();
7632 let receiver = managed.incoming_resources.first_mut().unwrap();
7633 let decrypt_fn = |ciphertext: &[u8]| -> Result<Vec<u8>, ()> {
7634 managed.engine.decrypt(ciphertext).map_err(|_| ())
7635 };
7636 receiver.tick(
7637 receiver.last_activity + late_delta,
7638 &decrypt_fn,
7639 &Bzip2Compressor,
7640 )
7641 };
7642 let late_actions =
7643 resp_mgr.process_resource_actions(&link_id, late_resource_actions, &mut rng);
7644 let retry_raw = late_actions
7645 .iter()
7646 .find_map(|a| match a {
7647 LinkManagerAction::SendPacket { raw, .. } => {
7648 let pkt = RawPacket::unpack(raw).ok()?;
7649 (pkt.context == constants::CONTEXT_RESOURCE_REQ).then_some(raw.clone())
7650 }
7651 _ => None,
7652 })
7653 .expect("receiver should emit a resource retry request after extended timeout");
7654
7655 {
7656 let managed = resp_mgr.links.get(&link_id).unwrap();
7657 let receiver = managed.incoming_resources.first().unwrap();
7658 assert_eq!(receiver.retries_left, retries_before - 1);
7659 }
7660
7661 let retry_pkt = RawPacket::unpack(&retry_raw).unwrap();
7662 let retry_plaintext = resp_mgr
7663 .links
7664 .get(&link_id)
7665 .unwrap()
7666 .engine
7667 .decrypt(&retry_pkt.data)
7668 .expect("retry request should decrypt");
7669 assert_eq!(retry_plaintext[0], constants::RESOURCE_HASHMAP_IS_EXHAUSTED);
7670
7671 let retry_to_sender = init_mgr.handle_local_delivery(
7674 retry_pkt.destination_hash,
7675 &retry_raw,
7676 retry_pkt.packet_hash,
7677 rns_core::transport::types::InterfaceId(0),
7678 &mut rng,
7679 );
7680 assert!(
7681 retry_to_sender.iter().any(|a| match a {
7682 LinkManagerAction::SendPacket { raw, .. } => RawPacket::unpack(raw)
7683 .map(|pkt| pkt.context == constants::CONTEXT_RESOURCE_HMU)
7684 .unwrap_or(false),
7685 _ => false,
7686 }),
7687 "sender should answer the exhausted retry request with a live HMU packet"
7688 );
7689 }
7690
7691 #[test]
7692 fn test_process_resource_actions_mapping() {
7693 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
7694 let mut rng = OsRng;
7695
7696 let actions = vec![
7698 ResourceAction::DataReceived {
7699 data: vec![1, 2, 3],
7700 metadata: Some(vec![4, 5]),
7701 },
7702 ResourceAction::Completed,
7703 ResourceAction::Failed(rns_core::resource::ResourceError::Timeout),
7704 ResourceAction::ProgressUpdate {
7705 received: 10,
7706 total: 20,
7707 },
7708 ResourceAction::TeardownLink,
7709 ];
7710
7711 let result = init_mgr.process_resource_actions(&link_id, actions, &mut rng);
7712
7713 assert!(matches!(
7714 result[0],
7715 LinkManagerAction::ResourceReceived { .. }
7716 ));
7717 assert!(matches!(
7718 result[1],
7719 LinkManagerAction::ResourceCompleted { .. }
7720 ));
7721 assert!(matches!(
7722 result[2],
7723 LinkManagerAction::ResourceFailed { .. }
7724 ));
7725 assert!(matches!(
7726 result[3],
7727 LinkManagerAction::ResourceProgress {
7728 received: 10,
7729 total: 20,
7730 ..
7731 }
7732 ));
7733 assert!(result
7734 .iter()
7735 .any(|action| matches!(action, LinkManagerAction::LinkClosed { .. })));
7736 }
7737
7738 #[test]
7739 fn test_link_state_empty() {
7740 let mgr = LinkManager::new();
7741 let fake_id = [0xAA; 16];
7742 assert!(mgr.link_state(&fake_id).is_none());
7743 }
7744
7745 #[test]
7746 fn test_large_response_resource_completes_as_response() {
7747 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
7748 let mut rng = OsRng;
7749
7750 let large_payload: Vec<u8> = (0..5000u32).map(|i| (i & 0xFF) as u8).collect();
7751 let response_value = rns_core::msgpack::pack(&rns_core::msgpack::Value::Bin(large_payload));
7752 resp_mgr.register_request_handler("/large", None, {
7753 let response_value = response_value.clone();
7754 move |_link_id, _path, _data, _remote| Some(response_value.clone())
7755 });
7756
7757 let req_actions = init_mgr.send_request(&link_id, "/large", b"\xc0", &mut rng);
7758 let req_raw = extract_any_send_packet(&req_actions);
7759 let req_pkt = RawPacket::unpack(&req_raw).unwrap();
7760 let request_id = req_pkt.get_truncated_hash();
7761 let resp_actions = resp_mgr.handle_local_delivery(
7762 req_pkt.destination_hash,
7763 &req_raw,
7764 req_pkt.packet_hash,
7765 rns_core::transport::types::InterfaceId(0),
7766 &mut rng,
7767 );
7768
7769 let mut pending: Vec<(char, LinkManagerAction)> =
7770 resp_actions.into_iter().map(|a| ('r', a)).collect();
7771 let mut rounds = 0;
7772 let mut received_response = None;
7773
7774 while !pending.is_empty() && rounds < 200 {
7775 rounds += 1;
7776 let mut next = Vec::new();
7777
7778 for (source, action) in pending.drain(..) {
7779 let LinkManagerAction::SendPacket { raw, .. } = action else {
7780 continue;
7781 };
7782 let pkt = RawPacket::unpack(&raw).unwrap();
7783 let target_actions = if source == 'r' {
7784 init_mgr.handle_local_delivery(
7785 pkt.destination_hash,
7786 &raw,
7787 pkt.packet_hash,
7788 rns_core::transport::types::InterfaceId(0),
7789 &mut rng,
7790 )
7791 } else {
7792 resp_mgr.handle_local_delivery(
7793 pkt.destination_hash,
7794 &raw,
7795 pkt.packet_hash,
7796 rns_core::transport::types::InterfaceId(0),
7797 &mut rng,
7798 )
7799 };
7800
7801 let target_source = if source == 'r' { 'i' } else { 'r' };
7802 for target_action in &target_actions {
7803 match target_action {
7804 LinkManagerAction::ResponseReceived {
7805 request_id: rid,
7806 data,
7807 ..
7808 } => {
7809 received_response = Some((*rid, data.clone()));
7810 }
7811 LinkManagerAction::ResourceReceived { .. } => {
7812 panic!("response resources must complete as ResponseReceived")
7813 }
7814 LinkManagerAction::ResourceAcceptQuery { .. } => {
7815 panic!("response resources must bypass application acceptance")
7816 }
7817 _ => {}
7818 }
7819 }
7820 next.extend(target_actions.into_iter().map(|a| (target_source, a)));
7821 }
7822
7823 pending = next;
7824 }
7825
7826 let (received_request_id, received_data) = received_response.unwrap_or_else(|| {
7827 panic!(
7828 "large response resource did not complete as ResponseReceived after {} rounds",
7829 rounds
7830 )
7831 });
7832 assert_eq!(received_request_id, request_id);
7833 assert_eq!(received_data, response_value);
7834 }
7835
7836 #[test]
7837 fn response_resource_at_exact_declared_size_limit_is_accepted() {
7838 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
7839 let mut rng = OsRng;
7840 let payload = deterministic_bytes(4096);
7841 let response_value =
7842 rns_core::msgpack::pack(&rns_core::msgpack::Value::Bin(payload.clone()));
7843 let expected_size = rns_core::msgpack::pack(&rns_core::msgpack::Value::Array(vec![
7844 rns_core::msgpack::Value::Bin(vec![0; 16]),
7845 rns_core::msgpack::Value::Bin(payload),
7846 ]))
7847 .len();
7848 resp_mgr.register_request_handler_response("/bounded-resource", None, {
7849 let response_value = response_value.clone();
7850 move |_, _, _, _| {
7851 Some(RequestResponse::Resource {
7852 data: response_value.clone(),
7853 metadata: None,
7854 auto_compress: false,
7855 })
7856 }
7857 });
7858
7859 let request_actions = init_mgr.send_request_with_max_response_size(
7860 &link_id,
7861 "/bounded-resource",
7862 b"\xc0",
7863 Some(expected_size),
7864 &mut rng,
7865 );
7866 let request_raw = extract_any_send_packet(&request_actions);
7867 let request = RawPacket::unpack(&request_raw).unwrap();
7868 let response_actions = resp_mgr.handle_local_delivery(
7869 request.destination_hash,
7870 &request_raw,
7871 request.packet_hash,
7872 rns_core::transport::types::InterfaceId(0),
7873 &mut rng,
7874 );
7875 let adv = first_resource_advertisement(&resp_mgr, &link_id, &response_actions);
7876 assert_eq!(adv.data_size as usize, expected_size);
7877 let adv_raw = extract_any_send_packet(&response_actions);
7878 let adv_packet = RawPacket::unpack(&adv_raw).unwrap();
7879 let actions = init_mgr.handle_local_delivery(
7880 adv_packet.destination_hash,
7881 &adv_raw,
7882 adv_packet.packet_hash,
7883 rns_core::transport::types::InterfaceId(0),
7884 &mut rng,
7885 );
7886
7887 assert!(!actions
7888 .iter()
7889 .any(|action| matches!(action, LinkManagerAction::RequestFailed { .. })));
7890 assert_eq!(init_mgr.links[&link_id].incoming_resources.len(), 1);
7891 }
7892
7893 #[test]
7894 fn response_resource_one_byte_over_declared_size_limit_is_rejected() {
7895 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
7896 let mut rng = OsRng;
7897 let payload = deterministic_bytes(4096);
7898 let response_value =
7899 rns_core::msgpack::pack(&rns_core::msgpack::Value::Bin(payload.clone()));
7900 let expected_size = rns_core::msgpack::pack(&rns_core::msgpack::Value::Array(vec![
7901 rns_core::msgpack::Value::Bin(vec![0; 16]),
7902 rns_core::msgpack::Value::Bin(payload),
7903 ]))
7904 .len();
7905 resp_mgr.register_request_handler_response("/bounded-resource", None, {
7906 let response_value = response_value.clone();
7907 move |_, _, _, _| {
7908 Some(RequestResponse::Resource {
7909 data: response_value.clone(),
7910 metadata: None,
7911 auto_compress: false,
7912 })
7913 }
7914 });
7915
7916 let request_actions = init_mgr.send_request_with_max_response_size(
7917 &link_id,
7918 "/bounded-resource",
7919 b"\xc0",
7920 Some(expected_size - 1),
7921 &mut rng,
7922 );
7923 let request_raw = extract_any_send_packet(&request_actions);
7924 let request = RawPacket::unpack(&request_raw).unwrap();
7925 let request_id = request.get_truncated_hash();
7926 let response_actions = resp_mgr.handle_local_delivery(
7927 request.destination_hash,
7928 &request_raw,
7929 request.packet_hash,
7930 rns_core::transport::types::InterfaceId(0),
7931 &mut rng,
7932 );
7933 let adv_raw = extract_any_send_packet(&response_actions);
7934 let adv_packet = RawPacket::unpack(&adv_raw).unwrap();
7935 let actions = init_mgr.handle_local_delivery(
7936 adv_packet.destination_hash,
7937 &adv_raw,
7938 adv_packet.packet_hash,
7939 rns_core::transport::types::InterfaceId(0),
7940 &mut rng,
7941 );
7942
7943 assert!(actions.iter().any(|action| matches!(
7944 action,
7945 LinkManagerAction::RequestFailed {
7946 request_id: failed_id,
7947 reason: RequestFailure::ResponseTooLarge { size, maximum },
7948 ..
7949 } if failed_id == &request_id
7950 && *size == expected_size as u64
7951 && *maximum == expected_size - 1
7952 )));
7953 assert!(init_mgr.links[&link_id].incoming_resources.is_empty());
7954 assert!(!init_mgr.links[&link_id]
7955 .pending_requests
7956 .contains_key(&request_id));
7957 }
7958
7959 #[test]
7960 fn test_response_resource_preserves_metadata() {
7961 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
7962 let mut rng = OsRng;
7963
7964 let payload = b"bundle-data".to_vec();
7965 let metadata = b"git-status-ok".to_vec();
7966 let response_value = rns_core::msgpack::pack(&rns_core::msgpack::Value::Bin(payload));
7967 resp_mgr.register_request_handler_response("/fetch", None, {
7968 let response_value = response_value.clone();
7969 let metadata = metadata.clone();
7970 move |_link_id, _path, _data, _remote| {
7971 Some(RequestResponse::Resource {
7972 data: response_value.clone(),
7973 metadata: Some(metadata.clone()),
7974 auto_compress: false,
7975 })
7976 }
7977 });
7978
7979 let req_actions = init_mgr.send_request(&link_id, "/fetch", b"\xc0", &mut rng);
7980 let req_raw = extract_any_send_packet(&req_actions);
7981 let req_pkt = RawPacket::unpack(&req_raw).unwrap();
7982 let request_id = req_pkt.get_truncated_hash();
7983 let resp_actions = resp_mgr.handle_local_delivery(
7984 req_pkt.destination_hash,
7985 &req_raw,
7986 req_pkt.packet_hash,
7987 rns_core::transport::types::InterfaceId(0),
7988 &mut rng,
7989 );
7990
7991 let mut pending: Vec<(char, LinkManagerAction)> =
7992 resp_actions.into_iter().map(|a| ('r', a)).collect();
7993 let mut received_response = None;
7994
7995 for _ in 0..200 {
7996 if pending.is_empty() || received_response.is_some() {
7997 break;
7998 }
7999
8000 let mut next = Vec::new();
8001 for (source, action) in pending.drain(..) {
8002 let LinkManagerAction::SendPacket { raw, .. } = action else {
8003 continue;
8004 };
8005 let pkt = RawPacket::unpack(&raw).unwrap();
8006 let target_actions = if source == 'r' {
8007 init_mgr.handle_local_delivery(
8008 pkt.destination_hash,
8009 &raw,
8010 pkt.packet_hash,
8011 rns_core::transport::types::InterfaceId(0),
8012 &mut rng,
8013 )
8014 } else {
8015 resp_mgr.handle_local_delivery(
8016 pkt.destination_hash,
8017 &raw,
8018 pkt.packet_hash,
8019 rns_core::transport::types::InterfaceId(0),
8020 &mut rng,
8021 )
8022 };
8023
8024 let target_source = if source == 'r' { 'i' } else { 'r' };
8025 for target_action in &target_actions {
8026 match target_action {
8027 LinkManagerAction::ResponseReceived {
8028 request_id: rid,
8029 data,
8030 metadata: response_metadata,
8031 ..
8032 } => {
8033 received_response =
8034 Some((*rid, data.clone(), response_metadata.clone()));
8035 }
8036 LinkManagerAction::ResourceReceived { .. } => {
8037 panic!("response resources must complete as ResponseReceived")
8038 }
8039 _ => {}
8040 }
8041 }
8042 next.extend(target_actions.into_iter().map(|a| (target_source, a)));
8043 }
8044 pending = next;
8045 }
8046
8047 let (received_request_id, received_data, received_metadata) = received_response
8048 .expect("resource response with metadata should complete as ResponseReceived");
8049 assert_eq!(received_request_id, request_id);
8050 assert_eq!(received_data, response_value);
8051 assert_eq!(received_metadata, Some(metadata));
8052 }
8053
8054 #[test]
8055 fn test_negotiated_mtu_response_uses_resource_before_global_mtu() {
8056 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
8057 let mut rng = OsRng;
8058
8059 init_mgr.set_link_mtu(&link_id, 300);
8060 resp_mgr.set_link_mtu(&link_id, 300);
8061
8062 let payload = vec![0xAB; 350];
8063 let response_value = rns_core::msgpack::pack(&rns_core::msgpack::Value::Bin(payload));
8064 resp_mgr.register_request_handler("/mtu", None, {
8065 let response_value = response_value.clone();
8066 move |_link_id, _path, _data, _remote| Some(response_value.clone())
8067 });
8068
8069 let req_actions = init_mgr.send_request(&link_id, "/mtu", b"\xc0", &mut rng);
8070 let req_raw = extract_any_send_packet(&req_actions);
8071 let req_pkt = RawPacket::unpack(&req_raw).unwrap();
8072 let resp_actions = resp_mgr.handle_local_delivery(
8073 req_pkt.destination_hash,
8074 &req_raw,
8075 req_pkt.packet_hash,
8076 rns_core::transport::types::InterfaceId(0),
8077 &mut rng,
8078 );
8079
8080 let mut has_resource_adv = false;
8081 let mut direct_response_len = None;
8082 for action in &resp_actions {
8083 if let LinkManagerAction::SendPacket { raw, .. } = action {
8084 let pkt = RawPacket::unpack(raw).unwrap();
8085 has_resource_adv |= pkt.context == constants::CONTEXT_RESOURCE_ADV;
8086 if pkt.context == constants::CONTEXT_RESPONSE {
8087 direct_response_len = Some(raw.len());
8088 }
8089 }
8090 }
8091
8092 assert!(
8093 has_resource_adv,
8094 "responses larger than the negotiated link MTU should use resource fallback"
8095 );
8096 assert!(
8097 direct_response_len.is_none(),
8098 "sent direct response of {} bytes on a 300 byte negotiated MTU",
8099 direct_response_len.unwrap_or_default()
8100 );
8101 }
8102
8103 #[test]
8104 fn test_large_management_response_uses_resource_fallback() {
8105 let (_init_mgr, mut resp_mgr, link_id) = setup_active_link();
8106 let mut rng = OsRng;
8107
8108 let payload = vec![0xBC; 5000];
8109 let response_value = rns_core::msgpack::pack(&rns_core::msgpack::Value::Bin(payload));
8110 let actions =
8111 resp_mgr.send_management_response(&link_id, &[0x55; 16], &response_value, &mut rng);
8112
8113 let mut has_resource_adv = false;
8114 let mut has_direct_response = false;
8115 for action in &actions {
8116 if let LinkManagerAction::SendPacket { raw, .. } = action {
8117 let pkt = RawPacket::unpack(raw).unwrap();
8118 has_resource_adv |= pkt.context == constants::CONTEXT_RESOURCE_ADV;
8119 has_direct_response |= pkt.context == constants::CONTEXT_RESPONSE;
8120 }
8121 }
8122
8123 assert!(
8124 has_resource_adv,
8125 "large management responses should advertise a response resource"
8126 );
8127 assert!(
8128 !has_direct_response,
8129 "large management responses should not use a direct CONTEXT_RESPONSE packet"
8130 );
8131 }
8132}