1use std::collections::HashMap;
11
12use rns_core::constants;
13use rns_core::destination::destination_hash;
14use rns_core::hash::truncated_hash;
15use rns_core::msgpack::{self, Value};
16use rns_core::transport::TransportEngine;
17
18use crate::interface::InterfaceEntry;
19use crate::time;
20
21pub fn status_path_hash() -> [u8; 16] {
23 truncated_hash(b"/status")
24}
25
26pub fn path_path_hash() -> [u8; 16] {
28 truncated_hash(b"/path")
29}
30
31pub fn list_path_hash() -> [u8; 16] {
33 truncated_hash(b"/list")
34}
35
36pub fn is_management_path(path_hash: &[u8; 16]) -> bool {
38 *path_hash == status_path_hash()
39 || *path_hash == path_path_hash()
40 || *path_hash == list_path_hash()
41}
42
43pub fn management_dest_hash(transport_identity_hash: &[u8; 16]) -> [u8; 16] {
47 destination_hash("rnstransport", &["remote", "management"], Some(transport_identity_hash))
48}
49
50pub fn blackhole_dest_hash(transport_identity_hash: &[u8; 16]) -> [u8; 16] {
54 destination_hash("rnstransport", &["info", "blackhole"], Some(transport_identity_hash))
55}
56
57pub fn probe_dest_hash(transport_identity_hash: &[u8; 16]) -> [u8; 16] {
61 destination_hash("rnstransport", &["probe"], Some(transport_identity_hash))
62}
63
64pub fn build_probe_announce(
68 identity: &rns_crypto::identity::Identity,
69 rng: &mut dyn rns_crypto::Rng,
70) -> Option<Vec<u8>> {
71 let identity_hash = *identity.hash();
72 let dest_hash = probe_dest_hash(&identity_hash);
73 let name_hash = rns_core::destination::name_hash("rnstransport", &["probe"]);
74 let mut random_hash = [0u8; 10];
75 rng.fill_bytes(&mut random_hash);
76
77 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
78 identity,
79 &dest_hash,
80 &name_hash,
81 &random_hash,
82 None,
83 None,
84 )
85 .ok()?;
86
87 let flags = rns_core::packet::PacketFlags {
88 header_type: constants::HEADER_1,
89 context_flag: constants::FLAG_UNSET,
90 transport_type: constants::TRANSPORT_BROADCAST,
91 destination_type: constants::DESTINATION_SINGLE,
92 packet_type: constants::PACKET_TYPE_ANNOUNCE,
93 };
94
95 let packet = rns_core::packet::RawPacket::pack(
96 flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data,
97 )
98 .ok()?;
99
100 Some(packet.raw)
101}
102
103#[derive(Debug, Clone)]
105pub struct ManagementConfig {
106 pub enable_remote_management: bool,
108 pub remote_management_allowed: Vec<[u8; 16]>,
110 pub publish_blackhole: bool,
112}
113
114impl Default for ManagementConfig {
115 fn default() -> Self {
116 ManagementConfig {
117 enable_remote_management: false,
118 remote_management_allowed: Vec::new(),
119 publish_blackhole: false,
120 }
121 }
122}
123
124pub fn handle_status_request(
129 data: &[u8],
130 engine: &TransportEngine,
131 interfaces: &HashMap<rns_core::transport::types::InterfaceId, InterfaceEntry>,
132 started: f64,
133 probe_responder_hash: Option<[u8; 16]>,
134) -> Option<Vec<u8>> {
135 let include_lstats = match msgpack::unpack_exact(data) {
137 Ok(Value::Array(arr)) if !arr.is_empty() => {
138 arr[0].as_bool().unwrap_or(false)
139 }
140 _ => false,
141 };
142
143 let mut iface_list = Vec::new();
145 let mut total_rxb: u64 = 0;
146 let mut total_txb: u64 = 0;
147
148 for (id, entry) in interfaces {
149 total_rxb += entry.stats.rxb;
150 total_txb += entry.stats.txb;
151
152 let mut ifstats: Vec<(&str, Value)> = Vec::new();
153 ifstats.push(("name", Value::Str(entry.info.name.clone())));
154 ifstats.push(("short_name", Value::Str(entry.info.name.clone())));
155 ifstats.push(("status", Value::Bool(entry.online)));
156 ifstats.push(("mode", Value::UInt(entry.info.mode as u64)));
157 ifstats.push(("rxb", Value::UInt(entry.stats.rxb)));
158 ifstats.push(("txb", Value::UInt(entry.stats.txb)));
159 if let Some(br) = entry.info.bitrate {
160 ifstats.push(("bitrate", Value::UInt(br)));
161 } else {
162 ifstats.push(("bitrate", Value::Nil));
163 }
164 ifstats.push(("incoming_announce_freq", Value::Float(entry.stats.incoming_announce_freq())));
165 ifstats.push(("outgoing_announce_freq", Value::Float(entry.stats.outgoing_announce_freq())));
166 ifstats.push(("held_announces", Value::UInt(engine.held_announce_count(id) as u64)));
167
168 ifstats.push(("ifac_signature", Value::Nil));
170 ifstats.push(("ifac_size", if entry.info.bitrate.is_some() {
171 Value::UInt(0)
172 } else {
173 Value::Nil
174 }));
175 ifstats.push(("ifac_netname", Value::Nil));
176
177 ifstats.push(("clients", Value::Nil));
179 ifstats.push(("announce_queue", Value::Nil));
180 ifstats.push(("rxs", Value::UInt(0)));
181 ifstats.push(("txs", Value::UInt(0)));
182
183 let map = ifstats.into_iter()
185 .map(|(k, v)| (Value::Str(k.into()), v))
186 .collect();
187 iface_list.push(Value::Map(map));
188 }
189
190 let mut stats: Vec<(&str, Value)> = Vec::new();
192 stats.push(("interfaces", Value::Array(iface_list)));
193 stats.push(("rxb", Value::UInt(total_rxb)));
194 stats.push(("txb", Value::UInt(total_txb)));
195 stats.push(("rxs", Value::UInt(0)));
196 stats.push(("txs", Value::UInt(0)));
197
198 if let Some(identity_hash) = engine.config().identity_hash {
199 stats.push(("transport_id", Value::Bin(identity_hash.to_vec())));
200 stats.push(("transport_uptime", Value::Float(time::now() - started)));
201 }
202 stats.push(("probe_responder", match probe_responder_hash {
203 Some(hash) => Value::Bin(hash.to_vec()),
204 None => Value::Nil,
205 }));
206 stats.push(("rss", Value::Nil));
207
208 let stats_map = stats.into_iter()
209 .map(|(k, v)| (Value::Str(k.into()), v))
210 .collect();
211
212 let mut response = vec![Value::Map(stats_map)];
214 if include_lstats {
215 let link_count = engine.link_table_count();
216 response.push(Value::UInt(link_count as u64));
217 }
218
219 Some(msgpack::pack(&Value::Array(response)))
220}
221
222pub fn handle_path_request(
228 data: &[u8],
229 engine: &TransportEngine,
230) -> Option<Vec<u8>> {
231 let arr = match msgpack::unpack_exact(data) {
232 Ok(Value::Array(arr)) if !arr.is_empty() => arr,
233 _ => return None,
234 };
235
236 let command = match &arr[0] {
237 Value::Str(s) => s.as_str(),
238 _ => return None,
239 };
240
241 let dest_filter: Option<[u8; 16]> = if arr.len() > 1 {
242 match &arr[1] {
243 Value::Bin(b) if b.len() == 16 => {
244 let mut h = [0u8; 16];
245 h.copy_from_slice(b);
246 Some(h)
247 }
248 _ => None,
249 }
250 } else {
251 None
252 };
253
254 let max_hops: Option<u8> = if arr.len() > 2 {
255 arr[2].as_uint().map(|v| v as u8)
256 } else {
257 None
258 };
259
260 match command {
261 "table" => {
262 let paths = engine.get_path_table(max_hops);
263 let mut entries = Vec::new();
264 for p in &paths {
265 if let Some(ref filter) = dest_filter {
266 if p.0 != *filter {
267 continue;
268 }
269 }
270 let entry = vec![
272 (Value::Str("hash".into()), Value::Bin(p.0.to_vec())),
273 (Value::Str("timestamp".into()), Value::Float(p.1)),
274 (Value::Str("via".into()), Value::Bin(p.2.to_vec())),
275 (Value::Str("hops".into()), Value::UInt(p.3 as u64)),
276 (Value::Str("expires".into()), Value::Float(p.4)),
277 (Value::Str("interface".into()), Value::Str(p.5.clone())),
278 ];
279 entries.push(Value::Map(entry));
280 }
281 Some(msgpack::pack(&Value::Array(entries)))
282 }
283 "rates" => {
284 let rates = engine.get_rate_table();
285 let mut entries = Vec::new();
286 for r in &rates {
287 if let Some(ref filter) = dest_filter {
288 if r.0 != *filter {
289 continue;
290 }
291 }
292 let timestamps: Vec<Value> = r.4.iter().map(|t| Value::Float(*t)).collect();
294 let entry = vec![
295 (Value::Str("hash".into()), Value::Bin(r.0.to_vec())),
296 (Value::Str("last".into()), Value::Float(r.1)),
297 (Value::Str("rate_violations".into()), Value::UInt(r.2 as u64)),
298 (Value::Str("blocked_until".into()), Value::Float(r.3)),
299 (Value::Str("timestamps".into()), Value::Array(timestamps)),
300 ];
301 entries.push(Value::Map(entry));
302 }
303 Some(msgpack::pack(&Value::Array(entries)))
304 }
305 _ => None,
306 }
307}
308
309pub fn handle_blackhole_list_request(
313 engine: &TransportEngine,
314) -> Option<Vec<u8>> {
315 let blackholed = engine.get_blackholed();
316 let mut map_entries = Vec::new();
317 for (hash, created, expires, reason) in &blackholed {
318 let mut entry = vec![
319 (Value::Str("created".into()), Value::Float(*created)),
320 (Value::Str("expires".into()), Value::Float(*expires)),
321 ];
322 if let Some(r) = reason {
323 entry.push((Value::Str("reason".into()), Value::Str(r.clone())));
324 }
325 map_entries.push((Value::Bin(hash.to_vec()), Value::Map(entry)));
326 }
327 Some(msgpack::pack(&Value::Map(map_entries)))
328}
329
330pub fn build_management_announce(
334 identity: &rns_crypto::identity::Identity,
335 rng: &mut dyn rns_crypto::Rng,
336) -> Option<Vec<u8>> {
337 let identity_hash = *identity.hash();
338 let dest_hash = management_dest_hash(&identity_hash);
339 let name_hash = rns_core::destination::name_hash("rnstransport", &["remote", "management"]);
340 let mut random_hash = [0u8; 10];
341 rng.fill_bytes(&mut random_hash);
342
343 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
344 identity,
345 &dest_hash,
346 &name_hash,
347 &random_hash,
348 None, None, )
351 .ok()?;
352
353 let flags = rns_core::packet::PacketFlags {
354 header_type: constants::HEADER_1,
355 context_flag: constants::FLAG_UNSET,
356 transport_type: constants::TRANSPORT_BROADCAST,
357 destination_type: constants::DESTINATION_SINGLE,
358 packet_type: constants::PACKET_TYPE_ANNOUNCE,
359 };
360
361 let packet = rns_core::packet::RawPacket::pack(
362 flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data,
363 )
364 .ok()?;
365
366 Some(packet.raw)
367}
368
369pub fn build_blackhole_announce(
373 identity: &rns_crypto::identity::Identity,
374 rng: &mut dyn rns_crypto::Rng,
375) -> Option<Vec<u8>> {
376 let identity_hash = *identity.hash();
377 let dest_hash = blackhole_dest_hash(&identity_hash);
378 let name_hash = rns_core::destination::name_hash("rnstransport", &["info", "blackhole"]);
379 let mut random_hash = [0u8; 10];
380 rng.fill_bytes(&mut random_hash);
381
382 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
383 identity,
384 &dest_hash,
385 &name_hash,
386 &random_hash,
387 None,
388 None,
389 )
390 .ok()?;
391
392 let flags = rns_core::packet::PacketFlags {
393 header_type: constants::HEADER_1,
394 context_flag: constants::FLAG_UNSET,
395 transport_type: constants::TRANSPORT_BROADCAST,
396 destination_type: constants::DESTINATION_SINGLE,
397 packet_type: constants::PACKET_TYPE_ANNOUNCE,
398 };
399
400 let packet = rns_core::packet::RawPacket::pack(
401 flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data,
402 )
403 .ok()?;
404
405 Some(packet.raw)
406}
407
408#[cfg(test)]
409mod tests {
410 use super::*;
411 use crate::interface::{InterfaceStats, Writer};
412 use crate::ifac::IfacState;
413 use rns_core::transport::types::{InterfaceId, InterfaceInfo, TransportConfig};
414 use std::io;
415
416 struct NullWriter;
417 impl Writer for NullWriter {
418 fn send_frame(&mut self, _data: &[u8]) -> io::Result<()> {
419 Ok(())
420 }
421 }
422
423 fn make_engine() -> TransportEngine {
424 TransportEngine::new(TransportConfig {
425 transport_enabled: true,
426 identity_hash: Some([0xAA; 16]),
427 })
428 }
429
430 fn make_interfaces() -> HashMap<InterfaceId, InterfaceEntry> {
431 let mut map = HashMap::new();
432 let id = InterfaceId(1);
433 let info = InterfaceInfo {
434 id,
435 name: "TestInterface".into(),
436 mode: constants::MODE_FULL,
437 out_capable: true,
438 in_capable: true,
439 bitrate: Some(115200),
440 announce_rate_target: None,
441 announce_rate_grace: 0,
442 announce_rate_penalty: 0.0,
443 announce_cap: constants::ANNOUNCE_CAP,
444 is_local_client: false,
445 wants_tunnel: false,
446 tunnel_id: None,
447 mtu: rns_core::constants::MTU as u32,
448 ia_freq: 0.0,
449 started: 0.0,
450 ingress_control: false,
451 };
452 map.insert(id, InterfaceEntry {
453 id,
454 info,
455 writer: Box::new(NullWriter),
456 online: true,
457 dynamic: false,
458 ifac: None,
459 stats: InterfaceStats {
460 rxb: 1234,
461 txb: 5678,
462 rx_packets: 10,
463 tx_packets: 20,
464 started: 1000.0,
465 ia_timestamps: vec![],
466 oa_timestamps: vec![],
467 },
468 interface_type: "TestInterface".to_string(),
469 });
470 map
471 }
472
473 #[test]
474 fn test_management_dest_hash() {
475 let id_hash = [0x42; 16];
476 let dh = management_dest_hash(&id_hash);
477 assert_eq!(dh, management_dest_hash(&id_hash));
479 assert_ne!(dh, management_dest_hash(&[0x43; 16]));
481 }
482
483 #[test]
484 fn test_blackhole_dest_hash() {
485 let id_hash = [0x42; 16];
486 let dh = blackhole_dest_hash(&id_hash);
487 assert_eq!(dh, blackhole_dest_hash(&id_hash));
488 assert_ne!(dh, management_dest_hash(&id_hash));
490 }
491
492 #[test]
493 fn test_path_hashes_distinct() {
494 let s = status_path_hash();
495 let p = path_path_hash();
496 let l = list_path_hash();
497 assert_ne!(s, p);
498 assert_ne!(s, l);
499 assert_ne!(p, l);
500 assert_ne!(s, [0u8; 16]);
502 }
503
504 #[test]
505 fn test_management_config_default() {
506 let config = ManagementConfig::default();
507 assert!(!config.enable_remote_management);
508 assert!(config.remote_management_allowed.is_empty());
509 assert!(!config.publish_blackhole);
510 }
511
512 #[test]
513 fn test_is_management_path() {
514 assert!(is_management_path(&status_path_hash()));
515 assert!(is_management_path(&path_path_hash()));
516 assert!(is_management_path(&list_path_hash()));
517 assert!(!is_management_path(&[0u8; 16]));
518 }
519
520 #[test]
521 fn test_status_request_basic() {
522 let engine = make_engine();
523 let interfaces = make_interfaces();
524 let started = time::now() - 100.0; let request = msgpack::pack(&Value::Array(vec![Value::Bool(false)]));
528 let response = handle_status_request(&request, &engine, &interfaces, started, None).unwrap();
529
530 let val = msgpack::unpack_exact(&response).unwrap();
532 match val {
533 Value::Array(arr) => {
534 assert_eq!(arr.len(), 1); match &arr[0] {
536 Value::Map(map) => {
537 let transport_id = map.iter()
539 .find(|(k, _)| *k == Value::Str("transport_id".into()))
540 .map(|(_, v)| v);
541 assert!(transport_id.is_some());
542
543 let rxb = map.iter()
545 .find(|(k, _)| *k == Value::Str("rxb".into()))
546 .map(|(_, v)| v.as_uint().unwrap());
547 assert_eq!(rxb, Some(1234));
548
549 let txb = map.iter()
550 .find(|(k, _)| *k == Value::Str("txb".into()))
551 .map(|(_, v)| v.as_uint().unwrap());
552 assert_eq!(txb, Some(5678));
553
554 let ifaces = map.iter()
556 .find(|(k, _)| *k == Value::Str("interfaces".into()))
557 .map(|(_, v)| v);
558 match ifaces {
559 Some(Value::Array(iface_arr)) => {
560 assert_eq!(iface_arr.len(), 1);
561 }
562 _ => panic!("Expected interfaces array"),
563 }
564
565 let uptime = map.iter()
567 .find(|(k, _)| *k == Value::Str("transport_uptime".into()))
568 .and_then(|(_, v)| v.as_float());
569 assert!(uptime.unwrap() >= 100.0);
570 }
571 _ => panic!("Expected map in response"),
572 }
573 }
574 _ => panic!("Expected array response"),
575 }
576 }
577
578 #[test]
579 fn test_status_request_with_lstats() {
580 let engine = make_engine();
581 let interfaces = make_interfaces();
582 let started = time::now();
583
584 let request = msgpack::pack(&Value::Array(vec![Value::Bool(true)]));
585 let response = handle_status_request(&request, &engine, &interfaces, started, None).unwrap();
586
587 let val = msgpack::unpack_exact(&response).unwrap();
588 match val {
589 Value::Array(arr) => {
590 assert_eq!(arr.len(), 2); assert_eq!(arr[1].as_uint(), Some(0)); }
593 _ => panic!("Expected array response"),
594 }
595 }
596
597 #[test]
598 fn test_status_request_empty_data() {
599 let engine = make_engine();
600 let interfaces = make_interfaces();
601 let started = time::now();
602
603 let response = handle_status_request(&[], &engine, &interfaces, started, None).unwrap();
605 let val = msgpack::unpack_exact(&response).unwrap();
606 match val {
607 Value::Array(arr) => assert_eq!(arr.len(), 1),
608 _ => panic!("Expected array response"),
609 }
610 }
611
612 #[test]
613 fn test_path_request_table() {
614 let engine = make_engine();
615
616 let request = msgpack::pack(&Value::Array(vec![Value::Str("table".into())]));
618 let response = handle_path_request(&request, &engine).unwrap();
619 let val = msgpack::unpack_exact(&response).unwrap();
620 match val {
621 Value::Array(arr) => assert_eq!(arr.len(), 0),
622 _ => panic!("Expected array"),
623 }
624 }
625
626 #[test]
627 fn test_path_request_rates() {
628 let engine = make_engine();
629
630 let request = msgpack::pack(&Value::Array(vec![Value::Str("rates".into())]));
631 let response = handle_path_request(&request, &engine).unwrap();
632 let val = msgpack::unpack_exact(&response).unwrap();
633 match val {
634 Value::Array(arr) => assert_eq!(arr.len(), 0),
635 _ => panic!("Expected array"),
636 }
637 }
638
639 #[test]
640 fn test_path_request_unknown_command() {
641 let engine = make_engine();
642
643 let request = msgpack::pack(&Value::Array(vec![Value::Str("unknown".into())]));
644 let response = handle_path_request(&request, &engine);
645 assert!(response.is_none());
646 }
647
648 #[test]
649 fn test_path_request_invalid_data() {
650 let engine = make_engine();
651 let response = handle_path_request(&[], &engine);
652 assert!(response.is_none());
653 }
654
655 #[test]
656 fn test_blackhole_list_empty() {
657 let engine = make_engine();
658 let response = handle_blackhole_list_request(&engine).unwrap();
659 let val = msgpack::unpack_exact(&response).unwrap();
660 match val {
661 Value::Map(entries) => assert_eq!(entries.len(), 0),
662 _ => panic!("Expected map"),
663 }
664 }
665
666 #[test]
669 fn test_build_management_announce() {
670 use rns_crypto::identity::Identity;
671 use rns_crypto::OsRng;
672
673 let identity = Identity::new(&mut OsRng);
674 let raw = build_management_announce(&identity, &mut OsRng);
675 assert!(raw.is_some(), "Should build management announce");
676
677 let raw = raw.unwrap();
678 let pkt = rns_core::packet::RawPacket::unpack(&raw).unwrap();
680 assert_eq!(pkt.flags.packet_type, constants::PACKET_TYPE_ANNOUNCE);
681 assert_eq!(pkt.flags.destination_type, constants::DESTINATION_SINGLE);
682 assert_eq!(pkt.destination_hash, management_dest_hash(identity.hash()));
683 }
684
685 #[test]
686 fn test_build_blackhole_announce() {
687 use rns_crypto::identity::Identity;
688 use rns_crypto::OsRng;
689
690 let identity = Identity::new(&mut OsRng);
691 let raw = build_blackhole_announce(&identity, &mut OsRng);
692 assert!(raw.is_some(), "Should build blackhole announce");
693
694 let raw = raw.unwrap();
695 let pkt = rns_core::packet::RawPacket::unpack(&raw).unwrap();
696 assert_eq!(pkt.flags.packet_type, constants::PACKET_TYPE_ANNOUNCE);
697 assert_eq!(pkt.destination_hash, blackhole_dest_hash(identity.hash()));
698 }
699
700 #[test]
701 fn test_management_announce_validates() {
702 use rns_crypto::identity::Identity;
703 use rns_crypto::OsRng;
704
705 let identity = Identity::new(&mut OsRng);
706 let raw = build_management_announce(&identity, &mut OsRng).unwrap();
707
708 let pkt = rns_core::packet::RawPacket::unpack(&raw).unwrap();
709
710 let validated = rns_core::announce::AnnounceData::unpack(&pkt.data, false);
712 assert!(validated.is_ok(), "Announce data should unpack");
713
714 let ann = validated.unwrap();
715 let result = ann.validate(&pkt.destination_hash);
716 assert!(result.is_ok(), "Announce should validate: {:?}", result.err());
717 }
718
719 #[test]
720 fn test_blackhole_announce_validates() {
721 use rns_crypto::identity::Identity;
722 use rns_crypto::OsRng;
723
724 let identity = Identity::new(&mut OsRng);
725 let raw = build_blackhole_announce(&identity, &mut OsRng).unwrap();
726
727 let pkt = rns_core::packet::RawPacket::unpack(&raw).unwrap();
728 let ann = rns_core::announce::AnnounceData::unpack(&pkt.data, false).unwrap();
729 let result = ann.validate(&pkt.destination_hash);
730 assert!(result.is_ok(), "Blackhole announce should validate: {:?}", result.err());
731 }
732
733 #[test]
734 fn test_probe_dest_hash() {
735 let id_hash = [0x42; 16];
736 let dh = probe_dest_hash(&id_hash);
737 assert_eq!(dh, probe_dest_hash(&id_hash));
739 assert_ne!(dh, probe_dest_hash(&[0x43; 16]));
741 assert_ne!(dh, management_dest_hash(&id_hash));
743 assert_ne!(dh, blackhole_dest_hash(&id_hash));
744 }
745
746 #[test]
747 fn test_build_probe_announce() {
748 use rns_crypto::identity::Identity;
749 use rns_crypto::OsRng;
750
751 let identity = Identity::new(&mut OsRng);
752 let raw = build_probe_announce(&identity, &mut OsRng);
753 assert!(raw.is_some(), "Should build probe announce");
754
755 let raw = raw.unwrap();
756 let pkt = rns_core::packet::RawPacket::unpack(&raw).unwrap();
757 assert_eq!(pkt.flags.packet_type, constants::PACKET_TYPE_ANNOUNCE);
758 assert_eq!(pkt.flags.destination_type, constants::DESTINATION_SINGLE);
759 assert_eq!(pkt.destination_hash, probe_dest_hash(identity.hash()));
760 }
761
762 #[test]
763 fn test_probe_announce_validates() {
764 use rns_crypto::identity::Identity;
765 use rns_crypto::OsRng;
766
767 let identity = Identity::new(&mut OsRng);
768 let raw = build_probe_announce(&identity, &mut OsRng).unwrap();
769 let pkt = rns_core::packet::RawPacket::unpack(&raw).unwrap();
770 let ann = rns_core::announce::AnnounceData::unpack(&pkt.data, false).unwrap();
771 let result = ann.validate(&pkt.destination_hash);
772 assert!(result.is_ok(), "Probe announce should validate: {:?}", result.err());
773 }
774
775 #[test]
776 fn test_management_announce_different_from_blackhole() {
777 use rns_crypto::identity::Identity;
778 use rns_crypto::OsRng;
779
780 let identity = Identity::new(&mut OsRng);
781 let mgmt_raw = build_management_announce(&identity, &mut OsRng).unwrap();
782 let bh_raw = build_blackhole_announce(&identity, &mut OsRng).unwrap();
783
784 let mgmt_pkt = rns_core::packet::RawPacket::unpack(&mgmt_raw).unwrap();
785 let bh_pkt = rns_core::packet::RawPacket::unpack(&bh_raw).unwrap();
786
787 assert_ne!(mgmt_pkt.destination_hash, bh_pkt.destination_hash,
788 "Management and blackhole should have different dest hashes");
789 }
790}