1use std::collections::HashMap;
10
11use rns_core::buffer::types::NoopCompressor;
12use rns_core::channel::Channel;
13use rns_core::constants;
14use rns_core::link::types::{LinkId, LinkState, TeardownReason};
15use rns_core::link::{LinkAction, LinkEngine, LinkMode};
16use rns_core::packet::{PacketFlags, RawPacket};
17use rns_core::resource::{ResourceAction, ResourceReceiver, ResourceSender};
18use rns_crypto::ed25519::Ed25519PrivateKey;
19use rns_crypto::Rng;
20
21use crate::time;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum ResourceStrategy {
26 AcceptNone,
28 AcceptAll,
30 AcceptApp,
32}
33
34impl Default for ResourceStrategy {
35 fn default() -> Self {
36 ResourceStrategy::AcceptNone
37 }
38}
39
40struct ManagedLink {
42 engine: LinkEngine,
43 channel: Option<Channel>,
44 dest_hash: [u8; 16],
46 remote_identity: Option<([u8; 16], [u8; 64])>,
48 dest_sig_pub_bytes: Option<[u8; 32]>,
50 incoming_resources: Vec<ResourceReceiver>,
52 outgoing_resources: Vec<ResourceSender>,
54 resource_strategy: ResourceStrategy,
56}
57
58struct LinkDestination {
60 dest_hash: [u8; 16],
61 sig_prv: Ed25519PrivateKey,
62 sig_pub_bytes: [u8; 32],
63}
64
65struct RequestHandlerEntry {
67 path: String,
69 path_hash: [u8; 16],
71 allowed_list: Option<Vec<[u8; 16]>>,
73 handler: Box<dyn Fn(LinkId, &str, &[u8], Option<&([u8; 16], [u8; 64])>) -> Option<Vec<u8>> + Send>,
75}
76
77#[derive(Debug)]
79pub enum LinkManagerAction {
80 SendPacket {
82 raw: Vec<u8>,
83 dest_type: u8,
84 attached_interface: Option<rns_core::transport::types::InterfaceId>,
85 },
86 LinkEstablished {
88 link_id: LinkId,
89 rtt: f64,
90 is_initiator: bool,
91 },
92 LinkClosed {
94 link_id: LinkId,
95 reason: Option<TeardownReason>,
96 },
97 RemoteIdentified {
99 link_id: LinkId,
100 identity_hash: [u8; 16],
101 public_key: [u8; 64],
102 },
103 RegisterLinkDest {
105 link_id: LinkId,
106 },
107 DeregisterLinkDest {
109 link_id: LinkId,
110 },
111 ManagementRequest {
114 link_id: LinkId,
115 path_hash: [u8; 16],
116 data: Vec<u8>,
118 request_id: [u8; 16],
120 remote_identity: Option<([u8; 16], [u8; 64])>,
121 },
122 ResourceReceived {
124 link_id: LinkId,
125 data: Vec<u8>,
126 metadata: Option<Vec<u8>>,
127 },
128 ResourceCompleted {
130 link_id: LinkId,
131 },
132 ResourceFailed {
134 link_id: LinkId,
135 error: String,
136 },
137 ResourceProgress {
139 link_id: LinkId,
140 received: usize,
141 total: usize,
142 },
143 ResourceAcceptQuery {
145 link_id: LinkId,
146 resource_hash: Vec<u8>,
147 transfer_size: u64,
148 has_metadata: bool,
149 },
150 ChannelMessageReceived {
152 link_id: LinkId,
153 msgtype: u16,
154 payload: Vec<u8>,
155 },
156 LinkDataReceived {
158 link_id: LinkId,
159 context: u8,
160 data: Vec<u8>,
161 },
162 ResponseReceived {
164 link_id: LinkId,
165 request_id: [u8; 16],
166 data: Vec<u8>,
167 },
168}
169
170pub struct LinkManager {
172 links: HashMap<LinkId, ManagedLink>,
173 link_destinations: HashMap<[u8; 16], LinkDestination>,
174 request_handlers: Vec<RequestHandlerEntry>,
175 management_paths: Vec<[u8; 16]>,
178}
179
180impl LinkManager {
181 pub fn new() -> Self {
183 LinkManager {
184 links: HashMap::new(),
185 link_destinations: HashMap::new(),
186 request_handlers: Vec::new(),
187 management_paths: Vec::new(),
188 }
189 }
190
191 pub fn register_management_path(&mut self, path_hash: [u8; 16]) {
195 if !self.management_paths.contains(&path_hash) {
196 self.management_paths.push(path_hash);
197 }
198 }
199
200 pub fn register_link_destination(
202 &mut self,
203 dest_hash: [u8; 16],
204 sig_prv: Ed25519PrivateKey,
205 sig_pub_bytes: [u8; 32],
206 ) {
207 self.link_destinations.insert(dest_hash, LinkDestination {
208 dest_hash,
209 sig_prv,
210 sig_pub_bytes,
211 });
212 }
213
214 pub fn deregister_link_destination(&mut self, dest_hash: &[u8; 16]) {
216 self.link_destinations.remove(dest_hash);
217 }
218
219 pub fn register_request_handler<F>(
225 &mut self,
226 path: &str,
227 allowed_list: Option<Vec<[u8; 16]>>,
228 handler: F,
229 ) where
230 F: Fn(LinkId, &str, &[u8], Option<&([u8; 16], [u8; 64])>) -> Option<Vec<u8>> + Send + 'static,
231 {
232 let path_hash = compute_path_hash(path);
233 self.request_handlers.push(RequestHandlerEntry {
234 path: path.to_string(),
235 path_hash,
236 allowed_list,
237 handler: Box::new(handler),
238 });
239 }
240
241 pub fn create_link(
249 &mut self,
250 dest_hash: &[u8; 16],
251 dest_sig_pub_bytes: &[u8; 32],
252 hops: u8,
253 rng: &mut dyn Rng,
254 ) -> (LinkId, Vec<LinkManagerAction>) {
255 let mode = LinkMode::Aes256Cbc;
256 let (mut engine, request_data) =
257 LinkEngine::new_initiator(dest_hash, hops, mode, Some(constants::MTU as u32), time::now(), rng);
258
259 let flags = PacketFlags {
261 header_type: constants::HEADER_1,
262 context_flag: constants::FLAG_UNSET,
263 transport_type: constants::TRANSPORT_BROADCAST,
264 destination_type: constants::DESTINATION_LINK,
265 packet_type: constants::PACKET_TYPE_LINKREQUEST,
266 };
267
268 let packet = match RawPacket::pack(
269 flags, 0, dest_hash, None, constants::CONTEXT_NONE, &request_data,
270 ) {
271 Ok(p) => p,
272 Err(_) => {
273 return ([0u8; 16], Vec::new());
275 }
276 };
277
278 engine.set_link_id_from_hashable(&packet.get_hashable_part(), request_data.len());
279 let link_id = *engine.link_id();
280
281 let managed = ManagedLink {
282 engine,
283 channel: None,
284 dest_hash: *dest_hash,
285 remote_identity: None,
286 dest_sig_pub_bytes: Some(*dest_sig_pub_bytes),
287 incoming_resources: Vec::new(),
288 outgoing_resources: Vec::new(),
289 resource_strategy: ResourceStrategy::default(),
290 };
291 self.links.insert(link_id, managed);
292
293 let mut actions = Vec::new();
294 actions.push(LinkManagerAction::RegisterLinkDest { link_id });
296 actions.push(LinkManagerAction::SendPacket {
298 raw: packet.raw,
299 dest_type: constants::DESTINATION_LINK,
300 attached_interface: None,
301 });
302
303 (link_id, actions)
304 }
305
306 pub fn handle_local_delivery(
312 &mut self,
313 dest_hash: [u8; 16],
314 raw: &[u8],
315 packet_hash: [u8; 32],
316 rng: &mut dyn Rng,
317 ) -> Vec<LinkManagerAction> {
318 let packet = match RawPacket::unpack(raw) {
319 Ok(p) => p,
320 Err(_) => return Vec::new(),
321 };
322
323 match packet.flags.packet_type {
324 constants::PACKET_TYPE_LINKREQUEST => {
325 self.handle_linkrequest(&dest_hash, &packet, rng)
326 }
327 constants::PACKET_TYPE_PROOF if packet.context == constants::CONTEXT_LRPROOF => {
328 self.handle_lrproof(&dest_hash, &packet, rng)
330 }
331 constants::PACKET_TYPE_DATA => {
332 self.handle_link_data(&dest_hash, &packet, packet_hash, rng)
333 }
334 _ => Vec::new(),
335 }
336 }
337
338 fn handle_linkrequest(
340 &mut self,
341 dest_hash: &[u8; 16],
342 packet: &RawPacket,
343 rng: &mut dyn Rng,
344 ) -> Vec<LinkManagerAction> {
345 let ld = match self.link_destinations.get(dest_hash) {
347 Some(ld) => ld,
348 None => return Vec::new(),
349 };
350
351 let hashable = packet.get_hashable_part();
352 let now = time::now();
353
354 let (engine, lrproof_data) = match LinkEngine::new_responder(
356 &ld.sig_prv,
357 &ld.sig_pub_bytes,
358 &packet.data,
359 &hashable,
360 dest_hash,
361 packet.hops,
362 now,
363 rng,
364 ) {
365 Ok(r) => r,
366 Err(e) => {
367 log::debug!("LINKREQUEST rejected: {}", e);
368 return Vec::new();
369 }
370 };
371
372 let link_id = *engine.link_id();
373
374 let managed = ManagedLink {
375 engine,
376 channel: None,
377 dest_hash: *dest_hash,
378 remote_identity: None,
379 dest_sig_pub_bytes: None,
380 incoming_resources: Vec::new(),
381 outgoing_resources: Vec::new(),
382 resource_strategy: ResourceStrategy::default(),
383 };
384 self.links.insert(link_id, managed);
385
386 let flags = PacketFlags {
388 header_type: constants::HEADER_1,
389 context_flag: constants::FLAG_UNSET,
390 transport_type: constants::TRANSPORT_BROADCAST,
391 destination_type: constants::DESTINATION_LINK,
392 packet_type: constants::PACKET_TYPE_PROOF,
393 };
394
395 let mut actions = Vec::new();
396
397 actions.push(LinkManagerAction::RegisterLinkDest { link_id });
399
400 if let Ok(pkt) = RawPacket::pack(
401 flags, 0, &link_id, None, constants::CONTEXT_LRPROOF, &lrproof_data,
402 ) {
403 actions.push(LinkManagerAction::SendPacket {
404 raw: pkt.raw,
405 dest_type: constants::DESTINATION_LINK,
406 attached_interface: None,
407 });
408 }
409
410 actions
411 }
412
413 fn handle_lrproof(
415 &mut self,
416 link_id_bytes: &[u8; 16],
417 packet: &RawPacket,
418 rng: &mut dyn Rng,
419 ) -> Vec<LinkManagerAction> {
420 let link = match self.links.get_mut(link_id_bytes) {
421 Some(l) => l,
422 None => return Vec::new(),
423 };
424
425 if link.engine.state() != LinkState::Pending || !link.engine.is_initiator() {
426 return Vec::new();
427 }
428
429 let dest_sig_pub_bytes = match link.dest_sig_pub_bytes {
431 Some(b) => b,
432 None => {
433 log::debug!("LRPROOF: no destination signing key available");
434 return Vec::new();
435 }
436 };
437
438 let now = time::now();
439 let (lrrtt_encrypted, link_actions) = match link.engine.handle_lrproof(
440 &packet.data,
441 &dest_sig_pub_bytes,
442 now,
443 rng,
444 ) {
445 Ok(r) => r,
446 Err(e) => {
447 log::debug!("LRPROOF validation failed: {}", e);
448 return Vec::new();
449 }
450 };
451
452 let link_id = *link.engine.link_id();
453 let mut actions = Vec::new();
454
455 actions.extend(self.process_link_actions(&link_id, &link_actions));
457
458 let flags = PacketFlags {
460 header_type: constants::HEADER_1,
461 context_flag: constants::FLAG_UNSET,
462 transport_type: constants::TRANSPORT_BROADCAST,
463 destination_type: constants::DESTINATION_LINK,
464 packet_type: constants::PACKET_TYPE_DATA,
465 };
466
467 if let Ok(pkt) = RawPacket::pack(
468 flags, 0, &link_id, None, constants::CONTEXT_LRRTT, &lrrtt_encrypted,
469 ) {
470 actions.push(LinkManagerAction::SendPacket {
471 raw: pkt.raw,
472 dest_type: constants::DESTINATION_LINK,
473 attached_interface: None,
474 });
475 }
476
477 if let Some(link) = self.links.get_mut(&link_id) {
479 if link.engine.state() == LinkState::Active {
480 let rtt = link.engine.rtt().unwrap_or(1.0);
481 link.channel = Some(Channel::new(rtt));
482 }
483 }
484
485 actions
486 }
487
488 fn handle_link_data(
494 &mut self,
495 link_id_bytes: &[u8; 16],
496 packet: &RawPacket,
497 _packet_hash: [u8; 32],
498 rng: &mut dyn Rng,
499 ) -> Vec<LinkManagerAction> {
500 enum LinkDataResult {
502 Lrrtt { link_id: LinkId, link_actions: Vec<LinkAction> },
503 Identify { link_id: LinkId, link_actions: Vec<LinkAction> },
504 Keepalive { link_id: LinkId, inbound_actions: Vec<LinkAction> },
505 LinkClose { link_id: LinkId, teardown_actions: Vec<LinkAction> },
506 Channel { link_id: LinkId, inbound_actions: Vec<LinkAction>, plaintext: Vec<u8> },
507 Request { link_id: LinkId, inbound_actions: Vec<LinkAction>, plaintext: Vec<u8> },
508 Response { link_id: LinkId, inbound_actions: Vec<LinkAction>, plaintext: Vec<u8> },
509 Generic { link_id: LinkId, inbound_actions: Vec<LinkAction>, plaintext: Vec<u8>, context: u8 },
510 ResourceAdv { link_id: LinkId, inbound_actions: Vec<LinkAction>, plaintext: Vec<u8> },
512 ResourceReq { link_id: LinkId, inbound_actions: Vec<LinkAction>, plaintext: Vec<u8> },
514 ResourceHmu { link_id: LinkId, inbound_actions: Vec<LinkAction>, plaintext: Vec<u8> },
516 ResourcePart { link_id: LinkId, inbound_actions: Vec<LinkAction>, raw_data: Vec<u8> },
518 ResourcePrf { link_id: LinkId, inbound_actions: Vec<LinkAction>, plaintext: Vec<u8> },
520 ResourceIcl { link_id: LinkId, inbound_actions: Vec<LinkAction> },
522 ResourceRcl { link_id: LinkId, inbound_actions: Vec<LinkAction> },
524 Error,
525 }
526
527 let result = {
528 let link = match self.links.get_mut(link_id_bytes) {
529 Some(l) => l,
530 None => return Vec::new(),
531 };
532
533 match packet.context {
534 constants::CONTEXT_LRRTT => {
535 match link.engine.handle_lrrtt(&packet.data, time::now()) {
536 Ok(link_actions) => {
537 let link_id = *link.engine.link_id();
538 LinkDataResult::Lrrtt { link_id, link_actions }
539 }
540 Err(e) => {
541 log::debug!("LRRTT handling failed: {}", e);
542 LinkDataResult::Error
543 }
544 }
545 }
546 constants::CONTEXT_LINKIDENTIFY => {
547 match link.engine.handle_identify(&packet.data) {
548 Ok(link_actions) => {
549 let link_id = *link.engine.link_id();
550 link.remote_identity = link.engine.remote_identity().cloned();
551 LinkDataResult::Identify { link_id, link_actions }
552 }
553 Err(e) => {
554 log::debug!("LINKIDENTIFY failed: {}", e);
555 LinkDataResult::Error
556 }
557 }
558 }
559 constants::CONTEXT_KEEPALIVE => {
560 let inbound_actions = link.engine.record_inbound(time::now());
561 let link_id = *link.engine.link_id();
562 LinkDataResult::Keepalive { link_id, inbound_actions }
563 }
564 constants::CONTEXT_LINKCLOSE => {
565 let teardown_actions = link.engine.handle_teardown();
566 let link_id = *link.engine.link_id();
567 LinkDataResult::LinkClose { link_id, teardown_actions }
568 }
569 constants::CONTEXT_CHANNEL => {
570 match link.engine.decrypt(&packet.data) {
571 Ok(plaintext) => {
572 let inbound_actions = link.engine.record_inbound(time::now());
573 let link_id = *link.engine.link_id();
574 LinkDataResult::Channel { link_id, inbound_actions, plaintext }
575 }
576 Err(_) => LinkDataResult::Error,
577 }
578 }
579 constants::CONTEXT_REQUEST => {
580 match link.engine.decrypt(&packet.data) {
581 Ok(plaintext) => {
582 let inbound_actions = link.engine.record_inbound(time::now());
583 let link_id = *link.engine.link_id();
584 LinkDataResult::Request { link_id, inbound_actions, plaintext }
585 }
586 Err(_) => LinkDataResult::Error,
587 }
588 }
589 constants::CONTEXT_RESPONSE => {
590 match link.engine.decrypt(&packet.data) {
591 Ok(plaintext) => {
592 let inbound_actions = link.engine.record_inbound(time::now());
593 let link_id = *link.engine.link_id();
594 LinkDataResult::Response { link_id, inbound_actions, plaintext }
595 }
596 Err(_) => LinkDataResult::Error,
597 }
598 }
599 constants::CONTEXT_RESOURCE_ADV => {
601 match link.engine.decrypt(&packet.data) {
602 Ok(plaintext) => {
603 let inbound_actions = link.engine.record_inbound(time::now());
604 let link_id = *link.engine.link_id();
605 LinkDataResult::ResourceAdv { link_id, inbound_actions, plaintext }
606 }
607 Err(_) => LinkDataResult::Error,
608 }
609 }
610 constants::CONTEXT_RESOURCE_REQ => {
611 match link.engine.decrypt(&packet.data) {
612 Ok(plaintext) => {
613 let inbound_actions = link.engine.record_inbound(time::now());
614 let link_id = *link.engine.link_id();
615 LinkDataResult::ResourceReq { link_id, inbound_actions, plaintext }
616 }
617 Err(_) => LinkDataResult::Error,
618 }
619 }
620 constants::CONTEXT_RESOURCE_HMU => {
621 match link.engine.decrypt(&packet.data) {
622 Ok(plaintext) => {
623 let inbound_actions = link.engine.record_inbound(time::now());
624 let link_id = *link.engine.link_id();
625 LinkDataResult::ResourceHmu { link_id, inbound_actions, plaintext }
626 }
627 Err(_) => LinkDataResult::Error,
628 }
629 }
630 constants::CONTEXT_RESOURCE => {
631 let inbound_actions = link.engine.record_inbound(time::now());
633 let link_id = *link.engine.link_id();
634 LinkDataResult::ResourcePart { link_id, inbound_actions, raw_data: packet.data.clone() }
635 }
636 constants::CONTEXT_RESOURCE_PRF => {
637 match link.engine.decrypt(&packet.data) {
638 Ok(plaintext) => {
639 let inbound_actions = link.engine.record_inbound(time::now());
640 let link_id = *link.engine.link_id();
641 LinkDataResult::ResourcePrf { link_id, inbound_actions, plaintext }
642 }
643 Err(_) => LinkDataResult::Error,
644 }
645 }
646 constants::CONTEXT_RESOURCE_ICL => {
647 let _ = link.engine.decrypt(&packet.data); let inbound_actions = link.engine.record_inbound(time::now());
649 let link_id = *link.engine.link_id();
650 LinkDataResult::ResourceIcl { link_id, inbound_actions }
651 }
652 constants::CONTEXT_RESOURCE_RCL => {
653 let _ = link.engine.decrypt(&packet.data); let inbound_actions = link.engine.record_inbound(time::now());
655 let link_id = *link.engine.link_id();
656 LinkDataResult::ResourceRcl { link_id, inbound_actions }
657 }
658 _ => {
659 match link.engine.decrypt(&packet.data) {
660 Ok(plaintext) => {
661 let inbound_actions = link.engine.record_inbound(time::now());
662 let link_id = *link.engine.link_id();
663 LinkDataResult::Generic { link_id, inbound_actions, plaintext, context: packet.context }
664 }
665 Err(_) => LinkDataResult::Error,
666 }
667 }
668 }
669 }; let mut actions = Vec::new();
673 match result {
674 LinkDataResult::Lrrtt { link_id, link_actions } => {
675 actions.extend(self.process_link_actions(&link_id, &link_actions));
676 if let Some(link) = self.links.get_mut(&link_id) {
678 if link.engine.state() == LinkState::Active {
679 let rtt = link.engine.rtt().unwrap_or(1.0);
680 link.channel = Some(Channel::new(rtt));
681 }
682 }
683 }
684 LinkDataResult::Identify { link_id, link_actions } => {
685 actions.extend(self.process_link_actions(&link_id, &link_actions));
686 }
687 LinkDataResult::Keepalive { link_id, inbound_actions } => {
688 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
689 }
690 LinkDataResult::LinkClose { link_id, teardown_actions } => {
691 actions.extend(self.process_link_actions(&link_id, &teardown_actions));
692 }
693 LinkDataResult::Channel { link_id, inbound_actions, plaintext } => {
694 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
695 if let Some(link) = self.links.get_mut(&link_id) {
697 if let Some(ref mut channel) = link.channel {
698 let chan_actions = channel.receive(&plaintext, time::now());
699 let _ = link;
701 actions.extend(self.process_channel_actions(&link_id, chan_actions, rng));
702 }
703 }
704 }
705 LinkDataResult::Request { link_id, inbound_actions, plaintext } => {
706 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
707 actions.extend(self.handle_request(&link_id, &plaintext, rng));
708 }
709 LinkDataResult::Response { link_id, inbound_actions, plaintext } => {
710 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
711 actions.extend(self.handle_response(&link_id, &plaintext));
713 }
714 LinkDataResult::Generic { link_id, inbound_actions, plaintext, context } => {
715 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
716 actions.push(LinkManagerAction::LinkDataReceived {
717 link_id,
718 context,
719 data: plaintext,
720 });
721 }
722 LinkDataResult::ResourceAdv { link_id, inbound_actions, plaintext } => {
723 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
724 actions.extend(self.handle_resource_adv(&link_id, &plaintext, rng));
725 }
726 LinkDataResult::ResourceReq { link_id, inbound_actions, plaintext } => {
727 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
728 actions.extend(self.handle_resource_req(&link_id, &plaintext, rng));
729 }
730 LinkDataResult::ResourceHmu { link_id, inbound_actions, plaintext } => {
731 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
732 actions.extend(self.handle_resource_hmu(&link_id, &plaintext, rng));
733 }
734 LinkDataResult::ResourcePart { link_id, inbound_actions, raw_data } => {
735 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
736 actions.extend(self.handle_resource_part(&link_id, &raw_data, rng));
737 }
738 LinkDataResult::ResourcePrf { link_id, inbound_actions, plaintext } => {
739 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
740 actions.extend(self.handle_resource_prf(&link_id, &plaintext));
741 }
742 LinkDataResult::ResourceIcl { link_id, inbound_actions } => {
743 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
744 actions.extend(self.handle_resource_icl(&link_id));
745 }
746 LinkDataResult::ResourceRcl { link_id, inbound_actions } => {
747 actions.extend(self.process_link_actions(&link_id, &inbound_actions));
748 actions.extend(self.handle_resource_rcl(&link_id));
749 }
750 LinkDataResult::Error => {}
751 }
752
753 actions
754 }
755
756 fn handle_request(
758 &mut self,
759 link_id: &LinkId,
760 plaintext: &[u8],
761 rng: &mut dyn Rng,
762 ) -> Vec<LinkManagerAction> {
763 use rns_core::msgpack::{self, Value};
764
765 let arr = match msgpack::unpack_exact(plaintext) {
767 Ok(Value::Array(arr)) if arr.len() >= 3 => arr,
768 _ => return Vec::new(),
769 };
770
771 let path_hash_bytes = match &arr[1] {
772 Value::Bin(b) if b.len() == 16 => b,
773 _ => return Vec::new(),
774 };
775 let mut path_hash = [0u8; 16];
776 path_hash.copy_from_slice(path_hash_bytes);
777
778 let request_id = rns_core::hash::truncated_hash(plaintext);
780
781 let request_data = msgpack::pack(&arr[2]);
783
784 if self.management_paths.contains(&path_hash) {
786 let remote_identity = self.links.get(link_id)
787 .and_then(|l| l.remote_identity)
788 .map(|(h, k)| (h, k));
789 return vec![LinkManagerAction::ManagementRequest {
790 link_id: *link_id,
791 path_hash,
792 data: request_data,
793 request_id,
794 remote_identity,
795 }];
796 }
797
798 let handler_idx = self.request_handlers.iter().position(|h| h.path_hash == path_hash);
800 let handler_idx = match handler_idx {
801 Some(i) => i,
802 None => return Vec::new(),
803 };
804
805 let remote_identity = self.links.get(link_id).and_then(|l| l.remote_identity.as_ref());
807 let handler = &self.request_handlers[handler_idx];
808 if let Some(ref allowed) = handler.allowed_list {
809 match remote_identity {
810 Some((identity_hash, _)) => {
811 if !allowed.contains(identity_hash) {
812 log::debug!("Request denied: identity not in allowed list");
813 return Vec::new();
814 }
815 }
816 None => {
817 log::debug!("Request denied: peer not identified");
818 return Vec::new();
819 }
820 }
821 }
822
823 let path = handler.path.clone();
825 let response = (handler.handler)(*link_id, &path, &request_data, remote_identity);
826
827 let mut actions = Vec::new();
828 if let Some(response_data) = response {
829 actions.extend(self.build_response_packet(link_id, &request_id, &response_data, rng));
830 }
831
832 actions
833 }
834
835 fn build_response_packet(
838 &self,
839 link_id: &LinkId,
840 request_id: &[u8; 16],
841 response_data: &[u8],
842 rng: &mut dyn Rng,
843 ) -> Vec<LinkManagerAction> {
844 use rns_core::msgpack::{self, Value};
845
846 let response_value = msgpack::unpack_exact(response_data)
848 .unwrap_or_else(|_| Value::Bin(response_data.to_vec()));
849
850 let response_array = Value::Array(vec![
851 Value::Bin(request_id.to_vec()),
852 response_value,
853 ]);
854 let response_plaintext = msgpack::pack(&response_array);
855
856 let mut actions = Vec::new();
857 if let Some(link) = self.links.get(link_id) {
858 if let Ok(encrypted) = link.engine.encrypt(&response_plaintext, rng) {
859 let flags = PacketFlags {
860 header_type: constants::HEADER_1,
861 context_flag: constants::FLAG_UNSET,
862 transport_type: constants::TRANSPORT_BROADCAST,
863 destination_type: constants::DESTINATION_LINK,
864 packet_type: constants::PACKET_TYPE_DATA,
865 };
866 if let Ok(pkt) = RawPacket::pack(
867 flags, 0, link_id, None, constants::CONTEXT_RESPONSE, &encrypted,
868 ) {
869 actions.push(LinkManagerAction::SendPacket {
870 raw: pkt.raw,
871 dest_type: constants::DESTINATION_LINK,
872 attached_interface: None,
873 });
874 }
875 }
876 }
877 actions
878 }
879
880 pub fn send_management_response(
883 &self,
884 link_id: &LinkId,
885 request_id: &[u8; 16],
886 response_data: &[u8],
887 rng: &mut dyn Rng,
888 ) -> Vec<LinkManagerAction> {
889 self.build_response_packet(link_id, request_id, response_data, rng)
890 }
891
892 pub fn send_request(
900 &self,
901 link_id: &LinkId,
902 path: &str,
903 data: &[u8],
904 rng: &mut dyn Rng,
905 ) -> Vec<LinkManagerAction> {
906 use rns_core::msgpack::{self, Value};
907
908 let link = match self.links.get(link_id) {
909 Some(l) => l,
910 None => return Vec::new(),
911 };
912
913 if link.engine.state() != LinkState::Active {
914 return Vec::new();
915 }
916
917 let path_hash = compute_path_hash(path);
918
919 let data_value = msgpack::unpack_exact(data)
921 .unwrap_or_else(|_| Value::Bin(data.to_vec()));
922
923 let request_array = Value::Array(vec![
925 Value::Float(time::now()),
926 Value::Bin(path_hash.to_vec()),
927 data_value,
928 ]);
929 let plaintext = msgpack::pack(&request_array);
930
931 let encrypted = match link.engine.encrypt(&plaintext, rng) {
932 Ok(e) => e,
933 Err(_) => return Vec::new(),
934 };
935
936 let flags = PacketFlags {
937 header_type: constants::HEADER_1,
938 context_flag: constants::FLAG_UNSET,
939 transport_type: constants::TRANSPORT_BROADCAST,
940 destination_type: constants::DESTINATION_LINK,
941 packet_type: constants::PACKET_TYPE_DATA,
942 };
943
944 let mut actions = Vec::new();
945 if let Ok(pkt) = RawPacket::pack(
946 flags, 0, link_id, None, constants::CONTEXT_REQUEST, &encrypted,
947 ) {
948 actions.push(LinkManagerAction::SendPacket {
949 raw: pkt.raw,
950 dest_type: constants::DESTINATION_LINK,
951 attached_interface: None,
952 });
953 }
954 actions
955 }
956
957 pub fn send_on_link(
959 &self,
960 link_id: &LinkId,
961 plaintext: &[u8],
962 context: u8,
963 rng: &mut dyn Rng,
964 ) -> Vec<LinkManagerAction> {
965 let link = match self.links.get(link_id) {
966 Some(l) => l,
967 None => return Vec::new(),
968 };
969
970 if link.engine.state() != LinkState::Active {
971 return Vec::new();
972 }
973
974 let encrypted = match link.engine.encrypt(plaintext, rng) {
975 Ok(e) => e,
976 Err(_) => return Vec::new(),
977 };
978
979 let flags = PacketFlags {
980 header_type: constants::HEADER_1,
981 context_flag: constants::FLAG_UNSET,
982 transport_type: constants::TRANSPORT_BROADCAST,
983 destination_type: constants::DESTINATION_LINK,
984 packet_type: constants::PACKET_TYPE_DATA,
985 };
986
987 let mut actions = Vec::new();
988 if let Ok(pkt) = RawPacket::pack(
989 flags, 0, link_id, None, context, &encrypted,
990 ) {
991 actions.push(LinkManagerAction::SendPacket {
992 raw: pkt.raw,
993 dest_type: constants::DESTINATION_LINK,
994 attached_interface: None,
995 });
996 }
997 actions
998 }
999
1000 pub fn identify(
1002 &self,
1003 link_id: &LinkId,
1004 identity: &rns_crypto::identity::Identity,
1005 rng: &mut dyn Rng,
1006 ) -> Vec<LinkManagerAction> {
1007 let link = match self.links.get(link_id) {
1008 Some(l) => l,
1009 None => return Vec::new(),
1010 };
1011
1012 let encrypted = match link.engine.build_identify(identity, rng) {
1013 Ok(e) => e,
1014 Err(_) => return Vec::new(),
1015 };
1016
1017 let flags = PacketFlags {
1018 header_type: constants::HEADER_1,
1019 context_flag: constants::FLAG_UNSET,
1020 transport_type: constants::TRANSPORT_BROADCAST,
1021 destination_type: constants::DESTINATION_LINK,
1022 packet_type: constants::PACKET_TYPE_DATA,
1023 };
1024
1025 let mut actions = Vec::new();
1026 if let Ok(pkt) = RawPacket::pack(
1027 flags, 0, link_id, None, constants::CONTEXT_LINKIDENTIFY, &encrypted,
1028 ) {
1029 actions.push(LinkManagerAction::SendPacket {
1030 raw: pkt.raw,
1031 dest_type: constants::DESTINATION_LINK,
1032 attached_interface: None,
1033 });
1034 }
1035 actions
1036 }
1037
1038 pub fn teardown_link(&mut self, link_id: &LinkId) -> Vec<LinkManagerAction> {
1040 let link = match self.links.get_mut(link_id) {
1041 Some(l) => l,
1042 None => return Vec::new(),
1043 };
1044
1045 let teardown_actions = link.engine.teardown();
1046 if let Some(ref mut channel) = link.channel {
1047 channel.shutdown();
1048 }
1049
1050 let mut actions = self.process_link_actions(link_id, &teardown_actions);
1051
1052 let flags = PacketFlags {
1054 header_type: constants::HEADER_1,
1055 context_flag: constants::FLAG_UNSET,
1056 transport_type: constants::TRANSPORT_BROADCAST,
1057 destination_type: constants::DESTINATION_LINK,
1058 packet_type: constants::PACKET_TYPE_DATA,
1059 };
1060 if let Ok(pkt) = RawPacket::pack(
1061 flags, 0, link_id, None, constants::CONTEXT_LINKCLOSE, &[],
1062 ) {
1063 actions.push(LinkManagerAction::SendPacket {
1064 raw: pkt.raw,
1065 dest_type: constants::DESTINATION_LINK,
1066 attached_interface: None,
1067 });
1068 }
1069
1070 actions
1071 }
1072
1073 fn handle_response(
1075 &self,
1076 link_id: &LinkId,
1077 plaintext: &[u8],
1078 ) -> Vec<LinkManagerAction> {
1079 use rns_core::msgpack;
1080
1081 let arr = match msgpack::unpack_exact(plaintext) {
1083 Ok(msgpack::Value::Array(arr)) if arr.len() >= 2 => arr,
1084 _ => return Vec::new(),
1085 };
1086
1087 let request_id_bytes = match &arr[0] {
1088 msgpack::Value::Bin(b) if b.len() == 16 => b,
1089 _ => return Vec::new(),
1090 };
1091 let mut request_id = [0u8; 16];
1092 request_id.copy_from_slice(request_id_bytes);
1093
1094 let response_data = msgpack::pack(&arr[1]);
1095
1096 vec![LinkManagerAction::ResponseReceived {
1097 link_id: *link_id,
1098 request_id,
1099 data: response_data,
1100 }]
1101 }
1102
1103 fn handle_resource_adv(
1105 &mut self,
1106 link_id: &LinkId,
1107 adv_plaintext: &[u8],
1108 rng: &mut dyn Rng,
1109 ) -> Vec<LinkManagerAction> {
1110 let link = match self.links.get_mut(link_id) {
1111 Some(l) => l,
1112 None => return Vec::new(),
1113 };
1114
1115 let link_rtt = link.engine.rtt().unwrap_or(1.0);
1116 let now = time::now();
1117
1118 let receiver = match ResourceReceiver::from_advertisement(
1119 adv_plaintext,
1120 constants::RESOURCE_SDU,
1121 link_rtt,
1122 now,
1123 None,
1124 None,
1125 ) {
1126 Ok(r) => r,
1127 Err(e) => {
1128 log::debug!("Resource ADV rejected: {}", e);
1129 return Vec::new();
1130 }
1131 };
1132
1133 let strategy = link.resource_strategy;
1134 let resource_hash = receiver.resource_hash.clone();
1135 let transfer_size = receiver.transfer_size;
1136 let has_metadata = receiver.has_metadata;
1137
1138 match strategy {
1139 ResourceStrategy::AcceptNone => {
1140 let reject_actions = {
1142 let mut r = receiver;
1143 r.reject()
1144 };
1145 self.process_resource_actions(link_id, reject_actions, rng)
1146 }
1147 ResourceStrategy::AcceptAll => {
1148 link.incoming_resources.push(receiver);
1149 let idx = link.incoming_resources.len() - 1;
1150 let resource_actions = link.incoming_resources[idx].accept(now);
1151 let _ = link;
1152 self.process_resource_actions(link_id, resource_actions, rng)
1153 }
1154 ResourceStrategy::AcceptApp => {
1155 link.incoming_resources.push(receiver);
1156 vec![LinkManagerAction::ResourceAcceptQuery {
1158 link_id: *link_id,
1159 resource_hash,
1160 transfer_size,
1161 has_metadata,
1162 }]
1163 }
1164 }
1165 }
1166
1167 pub fn accept_resource(
1169 &mut self,
1170 link_id: &LinkId,
1171 resource_hash: &[u8],
1172 accept: bool,
1173 rng: &mut dyn Rng,
1174 ) -> Vec<LinkManagerAction> {
1175 let link = match self.links.get_mut(link_id) {
1176 Some(l) => l,
1177 None => return Vec::new(),
1178 };
1179
1180 let now = time::now();
1181 let idx = link.incoming_resources.iter().position(|r| r.resource_hash == resource_hash);
1182 let idx = match idx {
1183 Some(i) => i,
1184 None => return Vec::new(),
1185 };
1186
1187 let resource_actions = if accept {
1188 link.incoming_resources[idx].accept(now)
1189 } else {
1190 link.incoming_resources[idx].reject()
1191 };
1192
1193 let _ = link;
1194 self.process_resource_actions(link_id, resource_actions, rng)
1195 }
1196
1197 fn handle_resource_req(
1199 &mut self,
1200 link_id: &LinkId,
1201 plaintext: &[u8],
1202 rng: &mut dyn Rng,
1203 ) -> Vec<LinkManagerAction> {
1204 let link = match self.links.get_mut(link_id) {
1205 Some(l) => l,
1206 None => return Vec::new(),
1207 };
1208
1209 let now = time::now();
1210 let mut all_actions = Vec::new();
1211 for sender in &mut link.outgoing_resources {
1212 let resource_actions = sender.handle_request(plaintext, now);
1213 if !resource_actions.is_empty() {
1214 all_actions.extend(resource_actions);
1215 break;
1216 }
1217 }
1218
1219 let _ = link;
1220 self.process_resource_actions(link_id, all_actions, rng)
1221 }
1222
1223 fn handle_resource_hmu(
1225 &mut self,
1226 link_id: &LinkId,
1227 plaintext: &[u8],
1228 rng: &mut dyn Rng,
1229 ) -> Vec<LinkManagerAction> {
1230 let link = match self.links.get_mut(link_id) {
1231 Some(l) => l,
1232 None => return Vec::new(),
1233 };
1234
1235 let now = time::now();
1236 let mut all_actions = Vec::new();
1237 for receiver in &mut link.incoming_resources {
1238 let resource_actions = receiver.handle_hashmap_update(plaintext, now);
1239 if !resource_actions.is_empty() {
1240 all_actions.extend(resource_actions);
1241 break;
1242 }
1243 }
1244
1245 let _ = link;
1246 self.process_resource_actions(link_id, all_actions, rng)
1247 }
1248
1249 fn handle_resource_part(
1251 &mut self,
1252 link_id: &LinkId,
1253 raw_data: &[u8],
1254 rng: &mut dyn Rng,
1255 ) -> Vec<LinkManagerAction> {
1256 let link = match self.links.get_mut(link_id) {
1257 Some(l) => l,
1258 None => return Vec::new(),
1259 };
1260
1261 let now = time::now();
1262 let mut all_actions = Vec::new();
1263 let mut assemble_idx = None;
1264
1265 for (idx, receiver) in link.incoming_resources.iter_mut().enumerate() {
1266 let resource_actions = receiver.receive_part(raw_data, now);
1267 if !resource_actions.is_empty() {
1268 if receiver.received_count == receiver.total_parts {
1270 assemble_idx = Some(idx);
1271 }
1272 all_actions.extend(resource_actions);
1273 break;
1274 }
1275 }
1276
1277 if let Some(idx) = assemble_idx {
1279 let decrypt_fn = |ciphertext: &[u8]| -> Result<Vec<u8>, ()> {
1280 link.engine.decrypt(ciphertext).map_err(|_| ())
1281 };
1282 let assemble_actions = link.incoming_resources[idx].assemble(&decrypt_fn, &NoopCompressor);
1283 all_actions.extend(assemble_actions);
1284 }
1285
1286 let _ = link;
1287 self.process_resource_actions(link_id, all_actions, rng)
1288 }
1289
1290 fn handle_resource_prf(
1292 &mut self,
1293 link_id: &LinkId,
1294 plaintext: &[u8],
1295 ) -> Vec<LinkManagerAction> {
1296 let link = match self.links.get_mut(link_id) {
1297 Some(l) => l,
1298 None => return Vec::new(),
1299 };
1300
1301 let now = time::now();
1302 let mut result_actions = Vec::new();
1303 for sender in &mut link.outgoing_resources {
1304 let resource_actions = sender.handle_proof(plaintext, now);
1305 if !resource_actions.is_empty() {
1306 result_actions.extend(resource_actions);
1307 break;
1308 }
1309 }
1310
1311 let mut actions = Vec::new();
1313 for ra in result_actions {
1314 match ra {
1315 ResourceAction::Completed => {
1316 actions.push(LinkManagerAction::ResourceCompleted { link_id: *link_id });
1317 }
1318 ResourceAction::Failed(e) => {
1319 actions.push(LinkManagerAction::ResourceFailed {
1320 link_id: *link_id,
1321 error: format!("{}", e),
1322 });
1323 }
1324 _ => {}
1325 }
1326 }
1327
1328 link.outgoing_resources.retain(|s| {
1330 s.status < rns_core::resource::ResourceStatus::Complete
1331 });
1332
1333 actions
1334 }
1335
1336 fn handle_resource_icl(
1338 &mut self,
1339 link_id: &LinkId,
1340 ) -> Vec<LinkManagerAction> {
1341 let link = match self.links.get_mut(link_id) {
1342 Some(l) => l,
1343 None => return Vec::new(),
1344 };
1345
1346 let mut actions = Vec::new();
1347 for receiver in &mut link.incoming_resources {
1348 let ra = receiver.handle_cancel();
1349 for a in ra {
1350 if let ResourceAction::Failed(ref e) = a {
1351 actions.push(LinkManagerAction::ResourceFailed {
1352 link_id: *link_id,
1353 error: format!("{}", e),
1354 });
1355 }
1356 }
1357 }
1358 link.incoming_resources.retain(|r| {
1359 r.status < rns_core::resource::ResourceStatus::Complete
1360 });
1361 actions
1362 }
1363
1364 fn handle_resource_rcl(
1366 &mut self,
1367 link_id: &LinkId,
1368 ) -> Vec<LinkManagerAction> {
1369 let link = match self.links.get_mut(link_id) {
1370 Some(l) => l,
1371 None => return Vec::new(),
1372 };
1373
1374 let mut actions = Vec::new();
1375 for sender in &mut link.outgoing_resources {
1376 let ra = sender.handle_reject();
1377 for a in ra {
1378 if let ResourceAction::Failed(ref e) = a {
1379 actions.push(LinkManagerAction::ResourceFailed {
1380 link_id: *link_id,
1381 error: format!("{}", e),
1382 });
1383 }
1384 }
1385 }
1386 link.outgoing_resources.retain(|s| {
1387 s.status < rns_core::resource::ResourceStatus::Complete
1388 });
1389 actions
1390 }
1391
1392 fn process_resource_actions(
1394 &self,
1395 link_id: &LinkId,
1396 actions: Vec<ResourceAction>,
1397 rng: &mut dyn Rng,
1398 ) -> Vec<LinkManagerAction> {
1399 let link = match self.links.get(link_id) {
1400 Some(l) => l,
1401 None => return Vec::new(),
1402 };
1403
1404 let mut result = Vec::new();
1405 for action in actions {
1406 match action {
1407 ResourceAction::SendAdvertisement(data) => {
1408 if let Ok(encrypted) = link.engine.encrypt(&data, rng) {
1410 result.extend(self.build_link_packet(
1411 link_id, constants::CONTEXT_RESOURCE_ADV, &encrypted,
1412 ));
1413 }
1414 }
1415 ResourceAction::SendPart(data) => {
1416 result.extend(self.build_link_packet(
1418 link_id, constants::CONTEXT_RESOURCE, &data,
1419 ));
1420 }
1421 ResourceAction::SendRequest(data) => {
1422 if let Ok(encrypted) = link.engine.encrypt(&data, rng) {
1423 result.extend(self.build_link_packet(
1424 link_id, constants::CONTEXT_RESOURCE_REQ, &encrypted,
1425 ));
1426 }
1427 }
1428 ResourceAction::SendHmu(data) => {
1429 if let Ok(encrypted) = link.engine.encrypt(&data, rng) {
1430 result.extend(self.build_link_packet(
1431 link_id, constants::CONTEXT_RESOURCE_HMU, &encrypted,
1432 ));
1433 }
1434 }
1435 ResourceAction::SendProof(data) => {
1436 if let Ok(encrypted) = link.engine.encrypt(&data, rng) {
1437 result.extend(self.build_link_packet(
1438 link_id, constants::CONTEXT_RESOURCE_PRF, &encrypted,
1439 ));
1440 }
1441 }
1442 ResourceAction::SendCancelInitiator(data) => {
1443 if let Ok(encrypted) = link.engine.encrypt(&data, rng) {
1444 result.extend(self.build_link_packet(
1445 link_id, constants::CONTEXT_RESOURCE_ICL, &encrypted,
1446 ));
1447 }
1448 }
1449 ResourceAction::SendCancelReceiver(data) => {
1450 if let Ok(encrypted) = link.engine.encrypt(&data, rng) {
1451 result.extend(self.build_link_packet(
1452 link_id, constants::CONTEXT_RESOURCE_RCL, &encrypted,
1453 ));
1454 }
1455 }
1456 ResourceAction::DataReceived { data, metadata } => {
1457 result.push(LinkManagerAction::ResourceReceived {
1458 link_id: *link_id,
1459 data,
1460 metadata,
1461 });
1462 }
1463 ResourceAction::Completed => {
1464 result.push(LinkManagerAction::ResourceCompleted { link_id: *link_id });
1465 }
1466 ResourceAction::Failed(e) => {
1467 result.push(LinkManagerAction::ResourceFailed {
1468 link_id: *link_id,
1469 error: format!("{}", e),
1470 });
1471 }
1472 ResourceAction::ProgressUpdate { received, total } => {
1473 result.push(LinkManagerAction::ResourceProgress {
1474 link_id: *link_id,
1475 received,
1476 total,
1477 });
1478 }
1479 }
1480 }
1481 result
1482 }
1483
1484 fn build_link_packet(
1486 &self,
1487 link_id: &LinkId,
1488 context: u8,
1489 data: &[u8],
1490 ) -> Vec<LinkManagerAction> {
1491 let flags = PacketFlags {
1492 header_type: constants::HEADER_1,
1493 context_flag: constants::FLAG_UNSET,
1494 transport_type: constants::TRANSPORT_BROADCAST,
1495 destination_type: constants::DESTINATION_LINK,
1496 packet_type: constants::PACKET_TYPE_DATA,
1497 };
1498 let mut actions = Vec::new();
1499 if let Ok(pkt) = RawPacket::pack(flags, 0, link_id, None, context, data) {
1500 actions.push(LinkManagerAction::SendPacket {
1501 raw: pkt.raw,
1502 dest_type: constants::DESTINATION_LINK,
1503 attached_interface: None,
1504 });
1505 }
1506 actions
1507 }
1508
1509 pub fn send_resource(
1511 &mut self,
1512 link_id: &LinkId,
1513 data: &[u8],
1514 metadata: Option<&[u8]>,
1515 rng: &mut dyn Rng,
1516 ) -> Vec<LinkManagerAction> {
1517 let link = match self.links.get_mut(link_id) {
1518 Some(l) => l,
1519 None => return Vec::new(),
1520 };
1521
1522 if link.engine.state() != LinkState::Active {
1523 return Vec::new();
1524 }
1525
1526 let link_rtt = link.engine.rtt().unwrap_or(1.0);
1527 let now = time::now();
1528
1529 let enc_rng = std::cell::RefCell::new(rns_crypto::OsRng);
1532 let encrypt_fn = |plaintext: &[u8]| -> Vec<u8> {
1533 link.engine.encrypt(plaintext, &mut *enc_rng.borrow_mut()).unwrap_or_else(|_| plaintext.to_vec())
1534 };
1535
1536 let sender = match ResourceSender::new(
1537 data,
1538 metadata,
1539 constants::RESOURCE_SDU,
1540 &encrypt_fn,
1541 &NoopCompressor,
1542 rng,
1543 now,
1544 false, false, None, 1, 1, None, link_rtt,
1551 6.0, ) {
1553 Ok(s) => s,
1554 Err(e) => {
1555 log::debug!("Failed to create ResourceSender: {}", e);
1556 return Vec::new();
1557 }
1558 };
1559
1560 let mut sender = sender;
1561 let adv_actions = sender.advertise(now);
1562 link.outgoing_resources.push(sender);
1563
1564 let _ = link;
1565 self.process_resource_actions(link_id, adv_actions, rng)
1566 }
1567
1568 pub fn set_resource_strategy(&mut self, link_id: &LinkId, strategy: ResourceStrategy) {
1570 if let Some(link) = self.links.get_mut(link_id) {
1571 link.resource_strategy = strategy;
1572 }
1573 }
1574
1575 pub fn send_channel_message(
1577 &mut self,
1578 link_id: &LinkId,
1579 msgtype: u16,
1580 payload: &[u8],
1581 rng: &mut dyn Rng,
1582 ) -> Vec<LinkManagerAction> {
1583 let link = match self.links.get_mut(link_id) {
1584 Some(l) => l,
1585 None => return Vec::new(),
1586 };
1587
1588 let channel = match link.channel {
1589 Some(ref mut ch) => ch,
1590 None => return Vec::new(),
1591 };
1592
1593 let link_mdu = constants::MDU; let now = time::now();
1595 let chan_actions = match channel.send(msgtype, payload, now, link_mdu) {
1596 Ok(a) => a,
1597 Err(e) => {
1598 log::debug!("Channel send failed: {:?}", e);
1599 return Vec::new();
1600 }
1601 };
1602
1603 let _ = link;
1604 self.process_channel_actions(link_id, chan_actions, rng)
1605 }
1606
1607 pub fn tick(&mut self, rng: &mut dyn Rng) -> Vec<LinkManagerAction> {
1609 let now = time::now();
1610 let mut all_actions = Vec::new();
1611
1612 let link_ids: Vec<LinkId> = self.links.keys().copied().collect();
1614
1615 for link_id in &link_ids {
1616 let link = match self.links.get_mut(link_id) {
1617 Some(l) => l,
1618 None => continue,
1619 };
1620
1621 let tick_actions = link.engine.tick(now);
1623 all_actions.extend(self.process_link_actions(link_id, &tick_actions));
1624
1625 let link = match self.links.get_mut(link_id) {
1627 Some(l) => l,
1628 None => continue,
1629 };
1630 if link.engine.needs_keepalive(now) {
1631 let flags = PacketFlags {
1633 header_type: constants::HEADER_1,
1634 context_flag: constants::FLAG_UNSET,
1635 transport_type: constants::TRANSPORT_BROADCAST,
1636 destination_type: constants::DESTINATION_LINK,
1637 packet_type: constants::PACKET_TYPE_DATA,
1638 };
1639 if let Ok(pkt) = RawPacket::pack(
1640 flags, 0, link_id, None, constants::CONTEXT_KEEPALIVE, &[],
1641 ) {
1642 all_actions.push(LinkManagerAction::SendPacket {
1643 raw: pkt.raw,
1644 dest_type: constants::DESTINATION_LINK,
1645 attached_interface: None,
1646 });
1647 link.engine.record_outbound(now, true);
1648 }
1649 }
1650 }
1651
1652 for link_id in &link_ids {
1654 let link = match self.links.get_mut(link_id) {
1655 Some(l) => l,
1656 None => continue,
1657 };
1658
1659 let mut sender_actions = Vec::new();
1661 for sender in &mut link.outgoing_resources {
1662 sender_actions.extend(sender.tick(now));
1663 }
1664
1665 let mut receiver_actions = Vec::new();
1667 for receiver in &mut link.incoming_resources {
1668 let decrypt_fn = |ciphertext: &[u8]| -> Result<Vec<u8>, ()> {
1669 link.engine.decrypt(ciphertext).map_err(|_| ())
1670 };
1671 receiver_actions.extend(receiver.tick(now, &decrypt_fn, &NoopCompressor));
1672 }
1673
1674 link.outgoing_resources.retain(|s| {
1676 s.status < rns_core::resource::ResourceStatus::Complete
1677 });
1678 link.incoming_resources.retain(|r| {
1679 r.status < rns_core::resource::ResourceStatus::Assembling
1680 });
1681
1682 let _ = link;
1683 all_actions.extend(self.process_resource_actions(link_id, sender_actions, rng));
1684 all_actions.extend(self.process_resource_actions(link_id, receiver_actions, rng));
1685 }
1686
1687 let closed: Vec<LinkId> = self.links.iter()
1689 .filter(|(_, l)| l.engine.state() == LinkState::Closed)
1690 .map(|(id, _)| *id)
1691 .collect();
1692 for id in closed {
1693 self.links.remove(&id);
1694 all_actions.push(LinkManagerAction::DeregisterLinkDest { link_id: id });
1695 }
1696
1697 all_actions
1698 }
1699
1700 pub fn is_link_destination(&self, dest_hash: &[u8; 16]) -> bool {
1702 self.links.contains_key(dest_hash) || self.link_destinations.contains_key(dest_hash)
1703 }
1704
1705 pub fn link_state(&self, link_id: &LinkId) -> Option<LinkState> {
1707 self.links.get(link_id).map(|l| l.engine.state())
1708 }
1709
1710 pub fn link_rtt(&self, link_id: &LinkId) -> Option<f64> {
1712 self.links.get(link_id).and_then(|l| l.engine.rtt())
1713 }
1714
1715 pub fn link_count(&self) -> usize {
1717 self.links.len()
1718 }
1719
1720 pub fn link_entries(&self) -> Vec<crate::event::LinkInfoEntry> {
1722 self.links
1723 .iter()
1724 .map(|(link_id, managed)| {
1725 let state = match managed.engine.state() {
1726 LinkState::Pending => "pending",
1727 LinkState::Handshake => "handshake",
1728 LinkState::Active => "active",
1729 LinkState::Stale => "stale",
1730 LinkState::Closed => "closed",
1731 };
1732 crate::event::LinkInfoEntry {
1733 link_id: *link_id,
1734 state: state.to_string(),
1735 is_initiator: managed.engine.is_initiator(),
1736 dest_hash: managed.dest_hash,
1737 remote_identity: managed.remote_identity.as_ref().map(|(h, _)| *h),
1738 rtt: managed.engine.rtt(),
1739 }
1740 })
1741 .collect()
1742 }
1743
1744 pub fn resource_entries(&self) -> Vec<crate::event::ResourceInfoEntry> {
1746 let mut entries = Vec::new();
1747 for (link_id, managed) in &self.links {
1748 for recv in &managed.incoming_resources {
1749 let (received, total) = recv.progress();
1750 entries.push(crate::event::ResourceInfoEntry {
1751 link_id: *link_id,
1752 direction: "incoming".to_string(),
1753 total_parts: total,
1754 transferred_parts: received,
1755 complete: received >= total && total > 0,
1756 });
1757 }
1758 for send in &managed.outgoing_resources {
1759 let total = send.total_parts();
1760 let sent = send.sent_parts;
1761 entries.push(crate::event::ResourceInfoEntry {
1762 link_id: *link_id,
1763 direction: "outgoing".to_string(),
1764 total_parts: total,
1765 transferred_parts: sent,
1766 complete: sent >= total && total > 0,
1767 });
1768 }
1769 }
1770 entries
1771 }
1772
1773 fn process_link_actions(&self, link_id: &LinkId, actions: &[LinkAction]) -> Vec<LinkManagerAction> {
1775 let mut result = Vec::new();
1776 for action in actions {
1777 match action {
1778 LinkAction::StateChanged { new_state, reason, .. } => {
1779 match new_state {
1780 LinkState::Closed => {
1781 result.push(LinkManagerAction::LinkClosed {
1782 link_id: *link_id,
1783 reason: *reason,
1784 });
1785 }
1786 _ => {}
1787 }
1788 }
1789 LinkAction::LinkEstablished { rtt, is_initiator, .. } => {
1790 result.push(LinkManagerAction::LinkEstablished {
1791 link_id: *link_id,
1792 rtt: *rtt,
1793 is_initiator: *is_initiator,
1794 });
1795 }
1796 LinkAction::RemoteIdentified { identity_hash, public_key, .. } => {
1797 result.push(LinkManagerAction::RemoteIdentified {
1798 link_id: *link_id,
1799 identity_hash: *identity_hash,
1800 public_key: *public_key,
1801 });
1802 }
1803 LinkAction::DataReceived { .. } => {
1804 }
1806 }
1807 }
1808 result
1809 }
1810
1811 fn process_channel_actions(
1813 &self,
1814 link_id: &LinkId,
1815 actions: Vec<rns_core::channel::ChannelAction>,
1816 rng: &mut dyn Rng,
1817 ) -> Vec<LinkManagerAction> {
1818 let mut result = Vec::new();
1819 for action in actions {
1820 match action {
1821 rns_core::channel::ChannelAction::SendOnLink { raw } => {
1822 if let Some(link) = self.links.get(link_id) {
1824 if let Ok(encrypted) = link.engine.encrypt(&raw, rng) {
1825 let flags = PacketFlags {
1826 header_type: constants::HEADER_1,
1827 context_flag: constants::FLAG_UNSET,
1828 transport_type: constants::TRANSPORT_BROADCAST,
1829 destination_type: constants::DESTINATION_LINK,
1830 packet_type: constants::PACKET_TYPE_DATA,
1831 };
1832 if let Ok(pkt) = RawPacket::pack(
1833 flags, 0, link_id, None, constants::CONTEXT_CHANNEL, &encrypted,
1834 ) {
1835 result.push(LinkManagerAction::SendPacket {
1836 raw: pkt.raw,
1837 dest_type: constants::DESTINATION_LINK,
1838 attached_interface: None,
1839 });
1840 }
1841 }
1842 }
1843 }
1844 rns_core::channel::ChannelAction::MessageReceived { msgtype, payload, .. } => {
1845 result.push(LinkManagerAction::ChannelMessageReceived {
1846 link_id: *link_id,
1847 msgtype,
1848 payload,
1849 });
1850 }
1851 rns_core::channel::ChannelAction::TeardownLink => {
1852 result.push(LinkManagerAction::LinkClosed {
1853 link_id: *link_id,
1854 reason: Some(TeardownReason::Timeout),
1855 });
1856 }
1857 }
1858 }
1859 result
1860 }
1861}
1862
1863fn compute_path_hash(path: &str) -> [u8; 16] {
1866 let full = rns_core::hash::full_hash(path.as_bytes());
1867 let mut result = [0u8; 16];
1868 result.copy_from_slice(&full[..16]);
1869 result
1870}
1871
1872#[cfg(test)]
1873mod tests {
1874 use super::*;
1875 use rns_crypto::identity::Identity;
1876 use rns_crypto::{FixedRng, OsRng};
1877
1878 fn make_rng(seed: u8) -> FixedRng {
1879 FixedRng::new(&[seed; 128])
1880 }
1881
1882 fn make_dest_keys(rng: &mut dyn Rng) -> (Ed25519PrivateKey, [u8; 32]) {
1883 let sig_prv = Ed25519PrivateKey::generate(rng);
1884 let sig_pub_bytes = sig_prv.public_key().public_bytes();
1885 (sig_prv, sig_pub_bytes)
1886 }
1887
1888 #[test]
1889 fn test_register_link_destination() {
1890 let mut mgr = LinkManager::new();
1891 let mut rng = make_rng(0x01);
1892 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
1893 let dest_hash = [0xDD; 16];
1894
1895 mgr.register_link_destination(dest_hash, sig_prv, sig_pub_bytes);
1896 assert!(mgr.is_link_destination(&dest_hash));
1897
1898 mgr.deregister_link_destination(&dest_hash);
1899 assert!(!mgr.is_link_destination(&dest_hash));
1900 }
1901
1902 #[test]
1903 fn test_create_link() {
1904 let mut mgr = LinkManager::new();
1905 let mut rng = OsRng;
1906 let dest_hash = [0xDD; 16];
1907
1908 let sig_pub_bytes = [0xAA; 32]; let (link_id, actions) = mgr.create_link(&dest_hash, &sig_pub_bytes, 1, &mut rng);
1910 assert_ne!(link_id, [0u8; 16]);
1911 assert_eq!(actions.len(), 2);
1913 assert!(matches!(actions[0], LinkManagerAction::RegisterLinkDest { .. }));
1914 assert!(matches!(actions[1], LinkManagerAction::SendPacket { .. }));
1915
1916 assert_eq!(mgr.link_state(&link_id), Some(LinkState::Pending));
1918 }
1919
1920 #[test]
1921 fn test_full_handshake_via_manager() {
1922 let mut rng = OsRng;
1923 let dest_hash = [0xDD; 16];
1924
1925 let mut responder_mgr = LinkManager::new();
1927 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
1928 responder_mgr.register_link_destination(dest_hash, sig_prv, sig_pub_bytes);
1929
1930 let mut initiator_mgr = LinkManager::new();
1932
1933 let (link_id, init_actions) = initiator_mgr.create_link(&dest_hash, &sig_pub_bytes, 1, &mut rng);
1935 assert_eq!(init_actions.len(), 2);
1936
1937 let linkrequest_raw = match &init_actions[1] {
1939 LinkManagerAction::SendPacket { raw, .. } => raw.clone(),
1940 _ => panic!("Expected SendPacket"),
1941 };
1942
1943 let lr_packet = RawPacket::unpack(&linkrequest_raw).unwrap();
1945
1946 let resp_actions = responder_mgr.handle_local_delivery(
1948 lr_packet.destination_hash,
1949 &linkrequest_raw,
1950 lr_packet.packet_hash,
1951 &mut rng,
1952 );
1953 assert!(resp_actions.len() >= 2);
1955 assert!(matches!(resp_actions[0], LinkManagerAction::RegisterLinkDest { .. }));
1956
1957 let lrproof_raw = match &resp_actions[1] {
1959 LinkManagerAction::SendPacket { raw, .. } => raw.clone(),
1960 _ => panic!("Expected SendPacket for LRPROOF"),
1961 };
1962
1963 let lrproof_packet = RawPacket::unpack(&lrproof_raw).unwrap();
1965 let init_actions2 = initiator_mgr.handle_local_delivery(
1966 lrproof_packet.destination_hash,
1967 &lrproof_raw,
1968 lrproof_packet.packet_hash,
1969 &mut rng,
1970 );
1971
1972 let has_established = init_actions2.iter().any(|a| matches!(a, LinkManagerAction::LinkEstablished { .. }));
1974 assert!(has_established, "Initiator should emit LinkEstablished");
1975
1976 let lrrtt_raw = init_actions2.iter().find_map(|a| match a {
1978 LinkManagerAction::SendPacket { raw, .. } => Some(raw.clone()),
1979 _ => None,
1980 }).expect("Should have LRRTT SendPacket");
1981
1982 let lrrtt_packet = RawPacket::unpack(&lrrtt_raw).unwrap();
1984 let resp_link_id = lrrtt_packet.destination_hash;
1985 let resp_actions2 = responder_mgr.handle_local_delivery(
1986 resp_link_id,
1987 &lrrtt_raw,
1988 lrrtt_packet.packet_hash,
1989 &mut rng,
1990 );
1991
1992 let has_established = resp_actions2.iter().any(|a| matches!(a, LinkManagerAction::LinkEstablished { .. }));
1993 assert!(has_established, "Responder should emit LinkEstablished");
1994
1995 assert_eq!(initiator_mgr.link_state(&link_id), Some(LinkState::Active));
1997 assert_eq!(responder_mgr.link_state(&link_id), Some(LinkState::Active));
1998
1999 assert!(initiator_mgr.link_rtt(&link_id).is_some());
2001 assert!(responder_mgr.link_rtt(&link_id).is_some());
2002 }
2003
2004 #[test]
2005 fn test_encrypted_data_exchange() {
2006 let mut rng = OsRng;
2007 let dest_hash = [0xDD; 16];
2008 let mut resp_mgr = LinkManager::new();
2009 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
2010 resp_mgr.register_link_destination(dest_hash, sig_prv, sig_pub_bytes);
2011 let mut init_mgr = LinkManager::new();
2012
2013 let (link_id, init_actions) = init_mgr.create_link(&dest_hash, &sig_pub_bytes, 1, &mut rng);
2015 let lr_raw = extract_send_packet(&init_actions);
2016 let lr_pkt = RawPacket::unpack(&lr_raw).unwrap();
2017 let resp_actions = resp_mgr.handle_local_delivery(lr_pkt.destination_hash, &lr_raw, lr_pkt.packet_hash, &mut rng);
2018 let lrproof_raw = extract_send_packet_at(&resp_actions, 1);
2019 let lrproof_pkt = RawPacket::unpack(&lrproof_raw).unwrap();
2020 let init_actions2 = init_mgr.handle_local_delivery(lrproof_pkt.destination_hash, &lrproof_raw, lrproof_pkt.packet_hash, &mut rng);
2021 let lrrtt_raw = extract_any_send_packet(&init_actions2);
2022 let lrrtt_pkt = RawPacket::unpack(&lrrtt_raw).unwrap();
2023 resp_mgr.handle_local_delivery(lrrtt_pkt.destination_hash, &lrrtt_raw, lrrtt_pkt.packet_hash, &mut rng);
2024
2025 let actions = init_mgr.send_on_link(&link_id, b"hello link!", constants::CONTEXT_NONE, &mut rng);
2027 assert_eq!(actions.len(), 1);
2028 assert!(matches!(actions[0], LinkManagerAction::SendPacket { .. }));
2029 }
2030
2031 #[test]
2032 fn test_request_response() {
2033 let mut rng = OsRng;
2034 let dest_hash = [0xDD; 16];
2035 let mut resp_mgr = LinkManager::new();
2036 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
2037 resp_mgr.register_link_destination(dest_hash, sig_prv, sig_pub_bytes);
2038
2039 resp_mgr.register_request_handler("/status", None, |_link_id, _path, _data, _remote| {
2041 Some(b"OK".to_vec())
2042 });
2043
2044 let mut init_mgr = LinkManager::new();
2045
2046 let (link_id, init_actions) = init_mgr.create_link(&dest_hash, &sig_pub_bytes, 1, &mut rng);
2048 let lr_raw = extract_send_packet(&init_actions);
2049 let lr_pkt = RawPacket::unpack(&lr_raw).unwrap();
2050 let resp_actions = resp_mgr.handle_local_delivery(lr_pkt.destination_hash, &lr_raw, lr_pkt.packet_hash, &mut rng);
2051 let lrproof_raw = extract_send_packet_at(&resp_actions, 1);
2052 let lrproof_pkt = RawPacket::unpack(&lrproof_raw).unwrap();
2053 let init_actions2 = init_mgr.handle_local_delivery(lrproof_pkt.destination_hash, &lrproof_raw, lrproof_pkt.packet_hash, &mut rng);
2054 let lrrtt_raw = extract_any_send_packet(&init_actions2);
2055 let lrrtt_pkt = RawPacket::unpack(&lrrtt_raw).unwrap();
2056 resp_mgr.handle_local_delivery(lrrtt_pkt.destination_hash, &lrrtt_raw, lrrtt_pkt.packet_hash, &mut rng);
2057
2058 let req_actions = init_mgr.send_request(&link_id, "/status", b"query", &mut rng);
2060 assert_eq!(req_actions.len(), 1);
2061
2062 let req_raw = extract_send_packet_from(&req_actions);
2064 let req_pkt = RawPacket::unpack(&req_raw).unwrap();
2065 let resp_actions = resp_mgr.handle_local_delivery(
2066 req_pkt.destination_hash, &req_raw, req_pkt.packet_hash, &mut rng,
2067 );
2068
2069 let has_response = resp_actions.iter().any(|a| matches!(a, LinkManagerAction::SendPacket { .. }));
2071 assert!(has_response, "Handler should produce a response packet");
2072 }
2073
2074 #[test]
2075 fn test_request_acl_deny_unidentified() {
2076 let mut rng = OsRng;
2077 let dest_hash = [0xDD; 16];
2078 let mut resp_mgr = LinkManager::new();
2079 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
2080 resp_mgr.register_link_destination(dest_hash, sig_prv, sig_pub_bytes);
2081
2082 resp_mgr.register_request_handler(
2084 "/restricted",
2085 Some(vec![[0xAA; 16]]),
2086 |_link_id, _path, _data, _remote| Some(b"secret".to_vec()),
2087 );
2088
2089 let mut init_mgr = LinkManager::new();
2090
2091 let (link_id, init_actions) = init_mgr.create_link(&dest_hash, &sig_pub_bytes, 1, &mut rng);
2093 let lr_raw = extract_send_packet(&init_actions);
2094 let lr_pkt = RawPacket::unpack(&lr_raw).unwrap();
2095 let resp_actions = resp_mgr.handle_local_delivery(lr_pkt.destination_hash, &lr_raw, lr_pkt.packet_hash, &mut rng);
2096 let lrproof_raw = extract_send_packet_at(&resp_actions, 1);
2097 let lrproof_pkt = RawPacket::unpack(&lrproof_raw).unwrap();
2098 let init_actions2 = init_mgr.handle_local_delivery(lrproof_pkt.destination_hash, &lrproof_raw, lrproof_pkt.packet_hash, &mut rng);
2099 let lrrtt_raw = extract_any_send_packet(&init_actions2);
2100 let lrrtt_pkt = RawPacket::unpack(&lrrtt_raw).unwrap();
2101 resp_mgr.handle_local_delivery(lrrtt_pkt.destination_hash, &lrrtt_raw, lrrtt_pkt.packet_hash, &mut rng);
2102
2103 let req_actions = init_mgr.send_request(&link_id, "/restricted", b"query", &mut rng);
2105 let req_raw = extract_send_packet_from(&req_actions);
2106 let req_pkt = RawPacket::unpack(&req_raw).unwrap();
2107 let resp_actions = resp_mgr.handle_local_delivery(
2108 req_pkt.destination_hash, &req_raw, req_pkt.packet_hash, &mut rng,
2109 );
2110
2111 let has_response = resp_actions.iter().any(|a| matches!(a, LinkManagerAction::SendPacket { .. }));
2113 assert!(!has_response, "Unidentified peer should be denied");
2114 }
2115
2116 #[test]
2117 fn test_teardown_link() {
2118 let mut rng = OsRng;
2119 let dest_hash = [0xDD; 16];
2120 let mut mgr = LinkManager::new();
2121
2122 let dummy_sig = [0xAA; 32];
2123 let (link_id, _) = mgr.create_link(&dest_hash, &dummy_sig, 1, &mut rng);
2124 assert_eq!(mgr.link_count(), 1);
2125
2126 let actions = mgr.teardown_link(&link_id);
2127 let has_close = actions.iter().any(|a| matches!(a, LinkManagerAction::LinkClosed { .. }));
2128 assert!(has_close);
2129
2130 let tick_actions = mgr.tick(&mut rng);
2132 let has_deregister = tick_actions.iter().any(|a| matches!(a, LinkManagerAction::DeregisterLinkDest { .. }));
2133 assert!(has_deregister);
2134 assert_eq!(mgr.link_count(), 0);
2135 }
2136
2137 #[test]
2138 fn test_identify_on_link() {
2139 let mut rng = OsRng;
2140 let dest_hash = [0xDD; 16];
2141 let mut resp_mgr = LinkManager::new();
2142 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
2143 resp_mgr.register_link_destination(dest_hash, sig_prv, sig_pub_bytes);
2144 let mut init_mgr = LinkManager::new();
2145
2146 let (link_id, init_actions) = init_mgr.create_link(&dest_hash, &sig_pub_bytes, 1, &mut rng);
2148 let lr_raw = extract_send_packet(&init_actions);
2149 let lr_pkt = RawPacket::unpack(&lr_raw).unwrap();
2150 let resp_actions = resp_mgr.handle_local_delivery(lr_pkt.destination_hash, &lr_raw, lr_pkt.packet_hash, &mut rng);
2151 let lrproof_raw = extract_send_packet_at(&resp_actions, 1);
2152 let lrproof_pkt = RawPacket::unpack(&lrproof_raw).unwrap();
2153 let init_actions2 = init_mgr.handle_local_delivery(lrproof_pkt.destination_hash, &lrproof_raw, lrproof_pkt.packet_hash, &mut rng);
2154 let lrrtt_raw = extract_any_send_packet(&init_actions2);
2155 let lrrtt_pkt = RawPacket::unpack(&lrrtt_raw).unwrap();
2156 resp_mgr.handle_local_delivery(lrrtt_pkt.destination_hash, &lrrtt_raw, lrrtt_pkt.packet_hash, &mut rng);
2157
2158 let identity = Identity::new(&mut rng);
2160 let id_actions = init_mgr.identify(&link_id, &identity, &mut rng);
2161 assert_eq!(id_actions.len(), 1);
2162
2163 let id_raw = extract_send_packet_from(&id_actions);
2165 let id_pkt = RawPacket::unpack(&id_raw).unwrap();
2166 let resp_actions = resp_mgr.handle_local_delivery(
2167 id_pkt.destination_hash, &id_raw, id_pkt.packet_hash, &mut rng,
2168 );
2169
2170 let has_identified = resp_actions.iter().any(|a| matches!(a, LinkManagerAction::RemoteIdentified { .. }));
2171 assert!(has_identified, "Responder should emit RemoteIdentified");
2172 }
2173
2174 #[test]
2175 fn test_path_hash_computation() {
2176 let h1 = compute_path_hash("/status");
2177 let h2 = compute_path_hash("/path");
2178 assert_ne!(h1, h2);
2179
2180 assert_eq!(h1, compute_path_hash("/status"));
2182 }
2183
2184 #[test]
2185 fn test_link_count() {
2186 let mut mgr = LinkManager::new();
2187 let mut rng = OsRng;
2188
2189 assert_eq!(mgr.link_count(), 0);
2190
2191 let dummy_sig = [0xAA; 32];
2192 mgr.create_link(&[0x11; 16], &dummy_sig, 1, &mut rng);
2193 assert_eq!(mgr.link_count(), 1);
2194
2195 mgr.create_link(&[0x22; 16], &dummy_sig, 1, &mut rng);
2196 assert_eq!(mgr.link_count(), 2);
2197 }
2198
2199 fn extract_send_packet(actions: &[LinkManagerAction]) -> Vec<u8> {
2202 extract_send_packet_at(actions, actions.len() - 1)
2203 }
2204
2205 fn extract_send_packet_at(actions: &[LinkManagerAction], idx: usize) -> Vec<u8> {
2206 match &actions[idx] {
2207 LinkManagerAction::SendPacket { raw, .. } => raw.clone(),
2208 other => panic!("Expected SendPacket at index {}, got {:?}", idx, other),
2209 }
2210 }
2211
2212 fn extract_any_send_packet(actions: &[LinkManagerAction]) -> Vec<u8> {
2213 actions.iter().find_map(|a| match a {
2214 LinkManagerAction::SendPacket { raw, .. } => Some(raw.clone()),
2215 _ => None,
2216 }).expect("Expected at least one SendPacket action")
2217 }
2218
2219 fn extract_send_packet_from(actions: &[LinkManagerAction]) -> Vec<u8> {
2220 extract_any_send_packet(actions)
2221 }
2222
2223 fn setup_active_link() -> (LinkManager, LinkManager, LinkId) {
2226 let mut rng = OsRng;
2227 let dest_hash = [0xDD; 16];
2228 let mut resp_mgr = LinkManager::new();
2229 let (sig_prv, sig_pub_bytes) = make_dest_keys(&mut rng);
2230 resp_mgr.register_link_destination(dest_hash, sig_prv, sig_pub_bytes);
2231 let mut init_mgr = LinkManager::new();
2232
2233 let (link_id, init_actions) = init_mgr.create_link(&dest_hash, &sig_pub_bytes, 1, &mut rng);
2234 let lr_raw = extract_send_packet(&init_actions);
2235 let lr_pkt = RawPacket::unpack(&lr_raw).unwrap();
2236 let resp_actions = resp_mgr.handle_local_delivery(
2237 lr_pkt.destination_hash, &lr_raw, lr_pkt.packet_hash, &mut rng,
2238 );
2239 let lrproof_raw = extract_send_packet_at(&resp_actions, 1);
2240 let lrproof_pkt = RawPacket::unpack(&lrproof_raw).unwrap();
2241 let init_actions2 = init_mgr.handle_local_delivery(
2242 lrproof_pkt.destination_hash, &lrproof_raw, lrproof_pkt.packet_hash, &mut rng,
2243 );
2244 let lrrtt_raw = extract_any_send_packet(&init_actions2);
2245 let lrrtt_pkt = RawPacket::unpack(&lrrtt_raw).unwrap();
2246 resp_mgr.handle_local_delivery(
2247 lrrtt_pkt.destination_hash, &lrrtt_raw, lrrtt_pkt.packet_hash, &mut rng,
2248 );
2249
2250 assert_eq!(init_mgr.link_state(&link_id), Some(LinkState::Active));
2251 assert_eq!(resp_mgr.link_state(&link_id), Some(LinkState::Active));
2252
2253 (init_mgr, resp_mgr, link_id)
2254 }
2255
2256 #[test]
2261 fn test_resource_strategy_default() {
2262 let mut mgr = LinkManager::new();
2263 let mut rng = OsRng;
2264 let dummy_sig = [0xAA; 32];
2265 let (link_id, _) = mgr.create_link(&[0x11; 16], &dummy_sig, 1, &mut rng);
2266
2267 let link = mgr.links.get(&link_id).unwrap();
2269 assert_eq!(link.resource_strategy, ResourceStrategy::AcceptNone);
2270 }
2271
2272 #[test]
2273 fn test_set_resource_strategy() {
2274 let mut mgr = LinkManager::new();
2275 let mut rng = OsRng;
2276 let dummy_sig = [0xAA; 32];
2277 let (link_id, _) = mgr.create_link(&[0x11; 16], &dummy_sig, 1, &mut rng);
2278
2279 mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
2280 assert_eq!(mgr.links.get(&link_id).unwrap().resource_strategy, ResourceStrategy::AcceptAll);
2281
2282 mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptApp);
2283 assert_eq!(mgr.links.get(&link_id).unwrap().resource_strategy, ResourceStrategy::AcceptApp);
2284 }
2285
2286 #[test]
2287 fn test_send_resource_on_active_link() {
2288 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
2289 let mut rng = OsRng;
2290
2291 let data = vec![0xAB; 100]; let actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
2294
2295 let has_send = actions.iter().any(|a| matches!(a, LinkManagerAction::SendPacket { .. }));
2297 assert!(has_send, "send_resource should emit advertisement SendPacket");
2298 }
2299
2300 #[test]
2301 fn test_send_resource_on_inactive_link() {
2302 let mut mgr = LinkManager::new();
2303 let mut rng = OsRng;
2304 let dummy_sig = [0xAA; 32];
2305 let (link_id, _) = mgr.create_link(&[0x11; 16], &dummy_sig, 1, &mut rng);
2306
2307 let actions = mgr.send_resource(&link_id, b"data", None, &mut rng);
2309 assert!(actions.is_empty(), "Cannot send resource on inactive link");
2310 }
2311
2312 #[test]
2313 fn test_resource_adv_rejected_by_accept_none() {
2314 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
2315 let mut rng = OsRng;
2316
2317 let data = vec![0xCD; 100];
2320 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
2321
2322 for action in &adv_actions {
2324 if let LinkManagerAction::SendPacket { raw, .. } = action {
2325 let pkt = RawPacket::unpack(raw).unwrap();
2326 let resp_actions = resp_mgr.handle_local_delivery(
2327 pkt.destination_hash, raw, pkt.packet_hash, &mut rng,
2328 );
2329 let has_resource_received = resp_actions.iter().any(|a|
2331 matches!(a, LinkManagerAction::ResourceReceived { .. })
2332 );
2333 assert!(!has_resource_received, "AcceptNone should not accept resource");
2334 }
2335 }
2336 }
2337
2338 #[test]
2339 fn test_resource_adv_accepted_by_accept_all() {
2340 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
2341 let mut rng = OsRng;
2342
2343 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
2345
2346 let data = vec![0xCD; 100];
2348 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
2349
2350 for action in &adv_actions {
2352 if let LinkManagerAction::SendPacket { raw, .. } = action {
2353 let pkt = RawPacket::unpack(raw).unwrap();
2354 let resp_actions = resp_mgr.handle_local_delivery(
2355 pkt.destination_hash, raw, pkt.packet_hash, &mut rng,
2356 );
2357 let has_send = resp_actions.iter().any(|a|
2359 matches!(a, LinkManagerAction::SendPacket { .. })
2360 );
2361 assert!(has_send, "AcceptAll should accept and request parts");
2362 }
2363 }
2364 }
2365
2366 #[test]
2367 fn test_resource_accept_app_query() {
2368 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
2369 let mut rng = OsRng;
2370
2371 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptApp);
2373
2374 let data = vec![0xCD; 100];
2376 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
2377
2378 let mut got_query = false;
2380 for action in &adv_actions {
2381 if let LinkManagerAction::SendPacket { raw, .. } = action {
2382 let pkt = RawPacket::unpack(raw).unwrap();
2383 let resp_actions = resp_mgr.handle_local_delivery(
2384 pkt.destination_hash, raw, pkt.packet_hash, &mut rng,
2385 );
2386 for a in &resp_actions {
2387 if matches!(a, LinkManagerAction::ResourceAcceptQuery { .. }) {
2388 got_query = true;
2389 }
2390 }
2391 }
2392 }
2393 assert!(got_query, "AcceptApp should emit ResourceAcceptQuery");
2394 }
2395
2396 #[test]
2397 fn test_resource_accept_app_accept() {
2398 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
2399 let mut rng = OsRng;
2400
2401 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptApp);
2402
2403 let data = vec![0xCD; 100];
2404 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
2405
2406 for action in &adv_actions {
2407 if let LinkManagerAction::SendPacket { raw, .. } = action {
2408 let pkt = RawPacket::unpack(raw).unwrap();
2409 let resp_actions = resp_mgr.handle_local_delivery(
2410 pkt.destination_hash, raw, pkt.packet_hash, &mut rng,
2411 );
2412 for a in &resp_actions {
2413 if let LinkManagerAction::ResourceAcceptQuery { link_id: lid, resource_hash, .. } = a {
2414 let accept_actions = resp_mgr.accept_resource(lid, resource_hash, true, &mut rng);
2416 let has_send = accept_actions.iter().any(|a|
2418 matches!(a, LinkManagerAction::SendPacket { .. })
2419 );
2420 assert!(has_send, "Accepting resource should produce request for parts");
2421 }
2422 }
2423 }
2424 }
2425 }
2426
2427 #[test]
2428 fn test_resource_accept_app_reject() {
2429 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
2430 let mut rng = OsRng;
2431
2432 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptApp);
2433
2434 let data = vec![0xCD; 100];
2435 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
2436
2437 for action in &adv_actions {
2438 if let LinkManagerAction::SendPacket { raw, .. } = action {
2439 let pkt = RawPacket::unpack(raw).unwrap();
2440 let resp_actions = resp_mgr.handle_local_delivery(
2441 pkt.destination_hash, raw, pkt.packet_hash, &mut rng,
2442 );
2443 for a in &resp_actions {
2444 if let LinkManagerAction::ResourceAcceptQuery { link_id: lid, resource_hash, .. } = a {
2445 let reject_actions = resp_mgr.accept_resource(lid, resource_hash, false, &mut rng);
2447 let has_resource_received = reject_actions.iter().any(|a|
2450 matches!(a, LinkManagerAction::ResourceReceived { .. })
2451 );
2452 assert!(!has_resource_received);
2453 }
2454 }
2455 }
2456 }
2457 }
2458
2459 #[test]
2460 fn test_resource_full_transfer() {
2461 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
2462 let mut rng = OsRng;
2463
2464 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
2466
2467 let original_data = b"Hello, Resource Transfer!".to_vec();
2469 let adv_actions = init_mgr.send_resource(&link_id, &original_data, None, &mut rng);
2470
2471 let mut pending: Vec<(char, LinkManagerAction)> = adv_actions.into_iter()
2474 .map(|a| ('i', a))
2475 .collect();
2476 let mut rounds = 0;
2477 let max_rounds = 50;
2478 let mut resource_received = false;
2479 let mut sender_completed = false;
2480
2481 while !pending.is_empty() && rounds < max_rounds {
2482 rounds += 1;
2483 let mut next: Vec<(char, LinkManagerAction)> = Vec::new();
2484
2485 for (source, action) in pending.drain(..) {
2486 if let LinkManagerAction::SendPacket { raw, .. } = action {
2487 let pkt = RawPacket::unpack(&raw).unwrap();
2488
2489 let target_actions = if source == 'i' {
2491 resp_mgr.handle_local_delivery(
2492 pkt.destination_hash, &raw, pkt.packet_hash, &mut rng,
2493 )
2494 } else {
2495 init_mgr.handle_local_delivery(
2496 pkt.destination_hash, &raw, pkt.packet_hash, &mut rng,
2497 )
2498 };
2499
2500 let target_source = if source == 'i' { 'r' } else { 'i' };
2501 for a in &target_actions {
2502 match a {
2503 LinkManagerAction::ResourceReceived { data, .. } => {
2504 assert_eq!(*data, original_data);
2505 resource_received = true;
2506 }
2507 LinkManagerAction::ResourceCompleted { .. } => {
2508 sender_completed = true;
2509 }
2510 _ => {}
2511 }
2512 }
2513 next.extend(target_actions.into_iter().map(|a| (target_source, a)));
2514 }
2515 }
2516 pending = next;
2517 }
2518
2519 assert!(resource_received, "Responder should receive resource data (rounds={})", rounds);
2520 assert!(sender_completed, "Sender should get completion proof (rounds={})", rounds);
2521 }
2522
2523 #[test]
2524 fn test_resource_cancel_icl() {
2525 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
2526 let mut rng = OsRng;
2527
2528 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
2529
2530 let data = vec![0xAB; 2000];
2532 let adv_actions = init_mgr.send_resource(&link_id, &data, None, &mut rng);
2533
2534 for action in &adv_actions {
2536 if let LinkManagerAction::SendPacket { raw, .. } = action {
2537 let pkt = RawPacket::unpack(raw).unwrap();
2538 resp_mgr.handle_local_delivery(
2539 pkt.destination_hash, raw, pkt.packet_hash, &mut rng,
2540 );
2541 }
2542 }
2543
2544 assert!(!resp_mgr.links.get(&link_id).unwrap().incoming_resources.is_empty());
2546
2547 let icl_actions = resp_mgr.handle_resource_icl(&link_id);
2549
2550 let has_failed = icl_actions.iter().any(|a| matches!(a, LinkManagerAction::ResourceFailed { .. }));
2552 assert!(has_failed, "ICL should produce ResourceFailed");
2553 }
2554
2555 #[test]
2556 fn test_resource_cancel_rcl() {
2557 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
2558 let mut rng = OsRng;
2559
2560 let data = vec![0xAB; 2000];
2562 init_mgr.send_resource(&link_id, &data, None, &mut rng);
2563
2564 assert!(!init_mgr.links.get(&link_id).unwrap().outgoing_resources.is_empty());
2566
2567 let rcl_actions = init_mgr.handle_resource_rcl(&link_id);
2569
2570 let has_failed = rcl_actions.iter().any(|a| matches!(a, LinkManagerAction::ResourceFailed { .. }));
2571 assert!(has_failed, "RCL should produce ResourceFailed");
2572 }
2573
2574 #[test]
2575 fn test_resource_tick_cleans_up() {
2576 let (mut init_mgr, _resp_mgr, link_id) = setup_active_link();
2577 let mut rng = OsRng;
2578
2579 let data = vec![0xAB; 100];
2580 init_mgr.send_resource(&link_id, &data, None, &mut rng);
2581
2582 assert!(!init_mgr.links.get(&link_id).unwrap().outgoing_resources.is_empty());
2583
2584 init_mgr.handle_resource_rcl(&link_id);
2586
2587 init_mgr.tick(&mut rng);
2589
2590 assert!(init_mgr.links.get(&link_id).unwrap().outgoing_resources.is_empty(),
2591 "Tick should clean up completed/failed outgoing resources");
2592 }
2593
2594 #[test]
2595 fn test_build_link_packet() {
2596 let (init_mgr, _resp_mgr, link_id) = setup_active_link();
2597
2598 let actions = init_mgr.build_link_packet(&link_id, constants::CONTEXT_RESOURCE, b"test data");
2599 assert_eq!(actions.len(), 1);
2600 if let LinkManagerAction::SendPacket { raw, dest_type, .. } = &actions[0] {
2601 let pkt = RawPacket::unpack(raw).unwrap();
2602 assert_eq!(pkt.context, constants::CONTEXT_RESOURCE);
2603 assert_eq!(*dest_type, constants::DESTINATION_LINK);
2604 } else {
2605 panic!("Expected SendPacket");
2606 }
2607 }
2608
2609 #[test]
2614 fn test_channel_message_delivery() {
2615 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
2616 let mut rng = OsRng;
2617
2618 let chan_actions = init_mgr.send_channel_message(&link_id, 42, b"channel data", &mut rng);
2620 assert!(!chan_actions.is_empty());
2621
2622 let mut got_channel_msg = false;
2624 for action in &chan_actions {
2625 if let LinkManagerAction::SendPacket { raw, .. } = action {
2626 let pkt = RawPacket::unpack(raw).unwrap();
2627 let resp_actions = resp_mgr.handle_local_delivery(
2628 pkt.destination_hash, raw, pkt.packet_hash, &mut rng,
2629 );
2630 for a in &resp_actions {
2631 if let LinkManagerAction::ChannelMessageReceived { msgtype, payload, .. } = a {
2632 assert_eq!(*msgtype, 42);
2633 assert_eq!(*payload, b"channel data");
2634 got_channel_msg = true;
2635 }
2636 }
2637 }
2638 }
2639 assert!(got_channel_msg, "Responder should receive channel message");
2640 }
2641
2642 #[test]
2643 fn test_generic_link_data_delivery() {
2644 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
2645 let mut rng = OsRng;
2646
2647 let actions = init_mgr.send_on_link(&link_id, b"raw stuff", 0x42, &mut rng);
2649 assert_eq!(actions.len(), 1);
2650
2651 let raw = extract_any_send_packet(&actions);
2653 let pkt = RawPacket::unpack(&raw).unwrap();
2654 let resp_actions = resp_mgr.handle_local_delivery(
2655 pkt.destination_hash, &raw, pkt.packet_hash, &mut rng,
2656 );
2657
2658 let has_data = resp_actions.iter().any(|a|
2659 matches!(a, LinkManagerAction::LinkDataReceived { context: 0x42, .. })
2660 );
2661 assert!(has_data, "Responder should receive LinkDataReceived for unknown context");
2662 }
2663
2664 #[test]
2665 fn test_response_delivery() {
2666 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
2667 let mut rng = OsRng;
2668
2669 resp_mgr.register_request_handler("/echo", None, |_link_id, _path, data, _remote| {
2671 Some(data.to_vec())
2672 });
2673
2674 let req_actions = init_mgr.send_request(&link_id, "/echo", b"\xc0", &mut rng); assert!(!req_actions.is_empty());
2677
2678 let req_raw = extract_any_send_packet(&req_actions);
2680 let req_pkt = RawPacket::unpack(&req_raw).unwrap();
2681 let resp_actions = resp_mgr.handle_local_delivery(
2682 req_pkt.destination_hash, &req_raw, req_pkt.packet_hash, &mut rng,
2683 );
2684 let has_resp_send = resp_actions.iter().any(|a| matches!(a, LinkManagerAction::SendPacket { .. }));
2685 assert!(has_resp_send, "Handler should produce response");
2686
2687 let resp_raw = extract_any_send_packet(&resp_actions);
2689 let resp_pkt = RawPacket::unpack(&resp_raw).unwrap();
2690 let init_actions = init_mgr.handle_local_delivery(
2691 resp_pkt.destination_hash, &resp_raw, resp_pkt.packet_hash, &mut rng,
2692 );
2693
2694 let has_response_received = init_actions.iter().any(|a|
2695 matches!(a, LinkManagerAction::ResponseReceived { .. })
2696 );
2697 assert!(has_response_received, "Initiator should receive ResponseReceived");
2698 }
2699
2700 #[test]
2701 fn test_send_channel_message_on_no_channel() {
2702 let mut mgr = LinkManager::new();
2703 let mut rng = OsRng;
2704 let dummy_sig = [0xAA; 32];
2705 let (link_id, _) = mgr.create_link(&[0x11; 16], &dummy_sig, 1, &mut rng);
2706
2707 let actions = mgr.send_channel_message(&link_id, 1, b"test", &mut rng);
2709 assert!(actions.is_empty(), "No channel on pending link");
2710 }
2711
2712 #[test]
2713 fn test_send_on_link_requires_active() {
2714 let mut mgr = LinkManager::new();
2715 let mut rng = OsRng;
2716 let dummy_sig = [0xAA; 32];
2717 let (link_id, _) = mgr.create_link(&[0x11; 16], &dummy_sig, 1, &mut rng);
2718
2719 let actions = mgr.send_on_link(&link_id, b"test", constants::CONTEXT_NONE, &mut rng);
2720 assert!(actions.is_empty(), "Cannot send on pending link");
2721 }
2722
2723 #[test]
2724 fn test_send_on_link_unknown_link() {
2725 let mgr = LinkManager::new();
2726 let mut rng = OsRng;
2727
2728 let actions = mgr.send_on_link(&[0xFF; 16], b"test", constants::CONTEXT_NONE, &mut rng);
2729 assert!(actions.is_empty());
2730 }
2731
2732 #[test]
2733 fn test_resource_full_transfer_large() {
2734 let (mut init_mgr, mut resp_mgr, link_id) = setup_active_link();
2735 let mut rng = OsRng;
2736
2737 resp_mgr.set_resource_strategy(&link_id, ResourceStrategy::AcceptAll);
2738
2739 let original_data: Vec<u8> = (0..2000u32).map(|i| {
2741 let pos = i as usize;
2742 (pos ^ (pos >> 8) ^ (pos >> 16)) as u8
2743 }).collect();
2744
2745 let adv_actions = init_mgr.send_resource(&link_id, &original_data, None, &mut rng);
2746
2747 let mut pending: Vec<(char, LinkManagerAction)> = adv_actions.into_iter()
2748 .map(|a| ('i', a))
2749 .collect();
2750 let mut rounds = 0;
2751 let max_rounds = 200;
2752 let mut resource_received = false;
2753 let mut sender_completed = false;
2754
2755 while !pending.is_empty() && rounds < max_rounds {
2756 rounds += 1;
2757 let mut next: Vec<(char, LinkManagerAction)> = Vec::new();
2758
2759 for (source, action) in pending.drain(..) {
2760 if let LinkManagerAction::SendPacket { raw, .. } = action {
2761 let pkt = match RawPacket::unpack(&raw) {
2762 Ok(p) => p,
2763 Err(_) => continue,
2764 };
2765
2766 let target_actions = if source == 'i' {
2767 resp_mgr.handle_local_delivery(
2768 pkt.destination_hash, &raw, pkt.packet_hash, &mut rng,
2769 )
2770 } else {
2771 init_mgr.handle_local_delivery(
2772 pkt.destination_hash, &raw, pkt.packet_hash, &mut rng,
2773 )
2774 };
2775
2776 let target_source = if source == 'i' { 'r' } else { 'i' };
2777 for a in &target_actions {
2778 match a {
2779 LinkManagerAction::ResourceReceived { data, .. } => {
2780 assert_eq!(*data, original_data);
2781 resource_received = true;
2782 }
2783 LinkManagerAction::ResourceCompleted { .. } => {
2784 sender_completed = true;
2785 }
2786 _ => {}
2787 }
2788 }
2789 next.extend(target_actions.into_iter().map(|a| (target_source, a)));
2790 }
2791 }
2792 pending = next;
2793 }
2794
2795 assert!(resource_received, "Should receive large resource (rounds={})", rounds);
2796 assert!(sender_completed, "Sender should complete (rounds={})", rounds);
2797 }
2798
2799 #[test]
2800 fn test_process_resource_actions_mapping() {
2801 let (init_mgr, _resp_mgr, link_id) = setup_active_link();
2802 let mut rng = OsRng;
2803
2804 let actions = vec![
2806 ResourceAction::DataReceived { data: vec![1, 2, 3], metadata: Some(vec![4, 5]) },
2807 ResourceAction::Completed,
2808 ResourceAction::Failed(rns_core::resource::ResourceError::Timeout),
2809 ResourceAction::ProgressUpdate { received: 10, total: 20 },
2810 ];
2811
2812 let result = init_mgr.process_resource_actions(&link_id, actions, &mut rng);
2813
2814 assert!(matches!(result[0], LinkManagerAction::ResourceReceived { .. }));
2815 assert!(matches!(result[1], LinkManagerAction::ResourceCompleted { .. }));
2816 assert!(matches!(result[2], LinkManagerAction::ResourceFailed { .. }));
2817 assert!(matches!(result[3], LinkManagerAction::ResourceProgress { received: 10, total: 20, .. }));
2818 }
2819}