1extern crate alloc;
10use alloc::vec::Vec;
11
12use crate::error::WireError;
13use crate::parameter_list::{Parameter, ParameterList, pid};
14use crate::property_list::WirePropertyList;
15use crate::wire_types::{Guid, Locator, LocatorKind, ProtocolVersion, VendorId};
16
17pub const ENCAPSULATION_PL_CDR_LE: [u8; 2] = [0x00, 0x03];
19
20pub mod endpoint_flag {
37 pub const PARTICIPANT_ANNOUNCER: u32 = 1 << 0;
43 pub const PARTICIPANT_DETECTOR: u32 = 1 << 1;
45 pub const PUBLICATIONS_ANNOUNCER: u32 = 1 << 2;
47 pub const PUBLICATIONS_DETECTOR: u32 = 1 << 3;
49 pub const SUBSCRIPTIONS_ANNOUNCER: u32 = 1 << 4;
51 pub const SUBSCRIPTIONS_DETECTOR: u32 = 1 << 5;
53
54 pub const PARTICIPANT_MESSAGE_DATA_WRITER: u32 = 1 << 10;
62 pub const PARTICIPANT_MESSAGE_DATA_READER: u32 = 1 << 11;
65
66 pub const TYPE_LOOKUP_REQUEST: u32 = 1 << 12;
74 pub const TYPE_LOOKUP_REPLY: u32 = 1 << 13;
78
79 pub const PUBLICATIONS_SECURE_WRITER: u32 = 1 << 16;
89 pub const PUBLICATIONS_SECURE_READER: u32 = 1 << 17;
92 pub const SUBSCRIPTIONS_SECURE_WRITER: u32 = 1 << 18;
95 pub const SUBSCRIPTIONS_SECURE_READER: u32 = 1 << 19;
98 pub const PARTICIPANT_MESSAGE_SECURE_WRITER: u32 = 1 << 20;
101 pub const PARTICIPANT_MESSAGE_SECURE_READER: u32 = 1 << 21;
104 pub const PARTICIPANT_STATELESS_MESSAGE_WRITER: u32 = 1 << 22;
107 pub const PARTICIPANT_STATELESS_MESSAGE_READER: u32 = 1 << 23;
110 pub const PARTICIPANT_VOLATILE_MESSAGE_SECURE_WRITER: u32 = 1 << 24;
113 pub const PARTICIPANT_VOLATILE_MESSAGE_SECURE_READER: u32 = 1 << 25;
116 pub const PARTICIPANT_SECURE_WRITER: u32 = 1 << 26;
119 pub const PARTICIPANT_SECURE_READER: u32 = 1 << 27;
122
123 pub const TOPICS_ANNOUNCER: u32 = 1 << 28;
130 pub const TOPICS_DETECTOR: u32 = 1 << 29;
133
134 pub const ALL_SECURE: u32 = PUBLICATIONS_SECURE_WRITER
141 | PUBLICATIONS_SECURE_READER
142 | SUBSCRIPTIONS_SECURE_WRITER
143 | SUBSCRIPTIONS_SECURE_READER
144 | PARTICIPANT_MESSAGE_SECURE_WRITER
145 | PARTICIPANT_MESSAGE_SECURE_READER
146 | PARTICIPANT_STATELESS_MESSAGE_WRITER
147 | PARTICIPANT_STATELESS_MESSAGE_READER
148 | PARTICIPANT_VOLATILE_MESSAGE_SECURE_WRITER
149 | PARTICIPANT_VOLATILE_MESSAGE_SECURE_READER
150 | PARTICIPANT_SECURE_WRITER
151 | PARTICIPANT_SECURE_READER;
152
153 pub const ALL_STANDARD: u32 = PARTICIPANT_ANNOUNCER
167 | PARTICIPANT_DETECTOR
168 | PUBLICATIONS_ANNOUNCER
169 | PUBLICATIONS_DETECTOR
170 | SUBSCRIPTIONS_ANNOUNCER
171 | SUBSCRIPTIONS_DETECTOR
172 | PARTICIPANT_MESSAGE_DATA_WRITER
173 | PARTICIPANT_MESSAGE_DATA_READER
174 | TYPE_LOOKUP_REQUEST
175 | TYPE_LOOKUP_REPLY;
176}
177
178pub use zerodds_qos::Duration;
184
185#[derive(Debug, Clone, PartialEq, Eq)]
187pub struct ParticipantBuiltinTopicData {
188 pub guid: Guid,
190 pub protocol_version: ProtocolVersion,
192 pub vendor_id: VendorId,
194 pub default_unicast_locator: Option<Locator>,
196 pub default_multicast_locator: Option<Locator>,
198 pub metatraffic_unicast_locator: Option<Locator>,
202 pub metatraffic_multicast_locator: Option<Locator>,
204 pub domain_id: Option<u32>,
207 pub builtin_endpoint_set: u32,
210 pub lease_duration: Duration,
213 pub user_data: Vec<u8>,
216 pub properties: WirePropertyList,
221 pub identity_token: Option<Vec<u8>>,
227 pub permissions_token: Option<Vec<u8>>,
230 pub identity_status_token: Option<Vec<u8>>,
234 pub sig_algo_info:
238 Option<crate::security_algo_info::ParticipantSecurityDigitalSignatureAlgorithmInfo>,
239 pub kx_algo_info:
243 Option<crate::security_algo_info::ParticipantSecurityKeyEstablishmentAlgorithmInfo>,
244 pub sym_cipher_algo_info:
248 Option<crate::security_algo_info::ParticipantSecuritySymmetricCipherAlgorithmInfo>,
249 pub participant_security_info:
255 Option<crate::participant_security_info::ParticipantSecurityInfo>,
256}
257
258impl ParticipantBuiltinTopicData {
259 #[must_use]
263 pub fn to_pl_cdr_le(&self) -> Vec<u8> {
264 let mut params = ParameterList::new();
265
266 let mut pv = Vec::with_capacity(4);
268 pv.extend_from_slice(&self.protocol_version.to_bytes());
269 pv.extend_from_slice(&[0, 0]);
270 params.push(Parameter::new(pid::PROTOCOL_VERSION, pv));
271
272 let mut vid = Vec::with_capacity(4);
274 vid.extend_from_slice(&self.vendor_id.to_bytes());
275 vid.extend_from_slice(&[0, 0]);
276 params.push(Parameter::new(pid::VENDOR_ID, vid));
277
278 params.push(Parameter::new(
280 pid::PARTICIPANT_GUID,
281 self.guid.to_bytes().to_vec(),
282 ));
283
284 if let Some(loc) = self.default_unicast_locator {
286 params.push(Parameter::new(
287 pid::DEFAULT_UNICAST_LOCATOR,
288 loc.to_bytes_le().to_vec(),
289 ));
290 }
291
292 if let Some(loc) = self.default_multicast_locator {
294 params.push(Parameter::new(
295 pid::DEFAULT_MULTICAST_LOCATOR,
296 loc.to_bytes_le().to_vec(),
297 ));
298 }
299
300 if let Some(loc) = self.metatraffic_unicast_locator {
302 params.push(Parameter::new(
303 pid::METATRAFFIC_UNICAST_LOCATOR,
304 loc.to_bytes_le().to_vec(),
305 ));
306 }
307
308 if let Some(loc) = self.metatraffic_multicast_locator {
310 params.push(Parameter::new(
311 pid::METATRAFFIC_MULTICAST_LOCATOR,
312 loc.to_bytes_le().to_vec(),
313 ));
314 }
315
316 if let Some(dom) = self.domain_id {
318 params.push(Parameter::new(pid::DOMAIN_ID, dom.to_le_bytes().to_vec()));
319 }
320
321 params.push(Parameter::new(
323 pid::BUILTIN_ENDPOINT_SET,
324 self.builtin_endpoint_set.to_le_bytes().to_vec(),
325 ));
326
327 params.push(Parameter::new(
329 pid::PARTICIPANT_LEASE_DURATION,
330 self.lease_duration.to_bytes_le().to_vec(),
331 ));
332
333 if !self.user_data.is_empty() {
335 if let Ok(v) = crate::publication_data::encode_octet_seq_le(&self.user_data) {
336 params.push(Parameter::new(pid::USER_DATA, v));
337 }
338 }
339
340 if let Some(blob) = self.identity_token.as_ref() {
344 params.push(Parameter::new(pid::IDENTITY_TOKEN, blob.clone()));
345 }
346 if let Some(blob) = self.permissions_token.as_ref() {
347 params.push(Parameter::new(pid::PERMISSIONS_TOKEN, blob.clone()));
348 }
349 if let Some(blob) = self.identity_status_token.as_ref() {
350 params.push(Parameter::new(pid::IDENTITY_STATUS_TOKEN, blob.clone()));
351 }
352 if let Some(psi) = self.participant_security_info.as_ref() {
356 params.push(Parameter::new(
357 pid::PARTICIPANT_SECURITY_INFO,
358 psi.to_le_bytes().to_vec(),
359 ));
360 }
361
362 if let Some(sig) = self.sig_algo_info.as_ref() {
365 params.push(Parameter::new(
366 pid::PARTICIPANT_SECURITY_DIGITAL_SIGNATURE_ALGORITHM_INFO,
367 sig.to_bytes(true).to_vec(),
368 ));
369 }
370 if let Some(kx) = self.kx_algo_info.as_ref() {
371 params.push(Parameter::new(
372 pid::PARTICIPANT_SECURITY_KEY_ESTABLISHMENT_ALGORITHM_INFO,
373 kx.to_bytes(true).to_vec(),
374 ));
375 }
376 if let Some(sym) = self.sym_cipher_algo_info.as_ref() {
377 params.push(Parameter::new(
378 pid::PARTICIPANT_SECURITY_SYMMETRIC_CIPHER_ALGORITHM_INFO,
379 sym.to_bytes(true).to_vec(),
380 ));
381 }
382
383 if !self.properties.is_empty() {
388 if let Ok(bytes) = self.properties.encode(true) {
389 params.push(Parameter::new(pid::PROPERTY_LIST, bytes));
390 }
391 }
392
393 let mut out = Vec::new();
395 out.extend_from_slice(&ENCAPSULATION_PL_CDR_LE);
396 out.extend_from_slice(&[0, 0]); out.extend_from_slice(¶ms.to_bytes(true));
398 out
399 }
400
401 #[must_use]
412 pub fn to_pl_cdr_be(&self) -> Vec<u8> {
413 let mut params = ParameterList::new();
414
415 let mut pv = Vec::with_capacity(4);
416 pv.extend_from_slice(&self.protocol_version.to_bytes());
417 pv.extend_from_slice(&[0, 0]);
418 params.push(Parameter::new(pid::PROTOCOL_VERSION, pv));
419
420 let mut vid = Vec::with_capacity(4);
421 vid.extend_from_slice(&self.vendor_id.to_bytes());
422 vid.extend_from_slice(&[0, 0]);
423 params.push(Parameter::new(pid::VENDOR_ID, vid));
424
425 params.push(Parameter::new(
426 pid::PARTICIPANT_GUID,
427 self.guid.to_bytes().to_vec(),
428 ));
429
430 for (id, loc) in [
431 (pid::DEFAULT_UNICAST_LOCATOR, self.default_unicast_locator),
432 (
433 pid::DEFAULT_MULTICAST_LOCATOR,
434 self.default_multicast_locator,
435 ),
436 (
437 pid::METATRAFFIC_UNICAST_LOCATOR,
438 self.metatraffic_unicast_locator,
439 ),
440 (
441 pid::METATRAFFIC_MULTICAST_LOCATOR,
442 self.metatraffic_multicast_locator,
443 ),
444 ] {
445 if let Some(loc) = loc {
446 params.push(Parameter::new(id, loc.to_bytes_be().to_vec()));
447 }
448 }
449
450 if let Some(dom) = self.domain_id {
451 params.push(Parameter::new(pid::DOMAIN_ID, dom.to_be_bytes().to_vec()));
452 }
453
454 params.push(Parameter::new(
455 pid::BUILTIN_ENDPOINT_SET,
456 self.builtin_endpoint_set.to_be_bytes().to_vec(),
457 ));
458
459 params.push(Parameter::new(
460 pid::PARTICIPANT_LEASE_DURATION,
461 self.lease_duration.to_bytes_be().to_vec(),
462 ));
463
464 if let Some(psi) = self.participant_security_info.as_ref() {
465 params.push(Parameter::new(
466 pid::PARTICIPANT_SECURITY_INFO,
467 psi.to_be_bytes().to_vec(),
468 ));
469 }
470 if let Some(sig) = self.sig_algo_info.as_ref() {
471 params.push(Parameter::new(
472 pid::PARTICIPANT_SECURITY_DIGITAL_SIGNATURE_ALGORITHM_INFO,
473 sig.to_bytes(false).to_vec(),
474 ));
475 }
476 if let Some(kx) = self.kx_algo_info.as_ref() {
477 params.push(Parameter::new(
478 pid::PARTICIPANT_SECURITY_KEY_ESTABLISHMENT_ALGORITHM_INFO,
479 kx.to_bytes(false).to_vec(),
480 ));
481 }
482 if let Some(sym) = self.sym_cipher_algo_info.as_ref() {
483 params.push(Parameter::new(
484 pid::PARTICIPANT_SECURITY_SYMMETRIC_CIPHER_ALGORITHM_INFO,
485 sym.to_bytes(false).to_vec(),
486 ));
487 }
488 if !self.properties.is_empty() {
489 if let Ok(bytes) = self.properties.encode(false) {
490 params.push(Parameter::new(pid::PROPERTY_LIST, bytes));
491 }
492 }
493
494 let mut out = Vec::new();
496 out.extend_from_slice(&[0x00, 0x02]);
497 out.extend_from_slice(&[0, 0]);
498 out.extend_from_slice(¶ms.to_bytes(false));
499 out
500 }
501
502 pub fn from_pl_cdr_le(bytes: &[u8]) -> Result<Self, WireError> {
508 if bytes.len() < 4 {
509 return Err(WireError::UnexpectedEof {
510 needed: 4,
511 offset: 0,
512 });
513 }
514 let little_endian = match &bytes[..2] {
517 b if b == ENCAPSULATION_PL_CDR_LE => true,
518 [0x00, 0x02] => false,
519 other => {
520 return Err(WireError::UnsupportedEncapsulation {
521 kind: [other[0], other[1]],
522 });
523 }
524 };
525 let pl = ParameterList::from_bytes(&bytes[4..], little_endian)?;
526
527 let guid = pl
528 .find(pid::PARTICIPANT_GUID)
529 .and_then(|p| {
530 if p.value.len() == 16 {
531 let mut g = [0u8; 16];
532 g.copy_from_slice(&p.value);
533 Some(Guid::from_bytes(g))
534 } else {
535 None
536 }
537 })
538 .ok_or(WireError::ValueOutOfRange {
539 message: "PARTICIPANT_GUID missing or wrong length",
540 })?;
541
542 let protocol_version = pl
543 .find(pid::PROTOCOL_VERSION)
544 .and_then(|p| {
545 if p.value.len() >= 2 {
546 let mut bs = [0u8; 2];
547 bs.copy_from_slice(&p.value[..2]);
548 Some(ProtocolVersion::from_bytes(bs))
549 } else {
550 None
551 }
552 })
553 .unwrap_or_default();
554
555 let vendor_id = pl
556 .find(pid::VENDOR_ID)
557 .and_then(|p| {
558 if p.value.len() >= 2 {
559 let mut bs = [0u8; 2];
560 bs.copy_from_slice(&p.value[..2]);
561 Some(VendorId::from_bytes(bs))
562 } else {
563 None
564 }
565 })
566 .unwrap_or(VendorId::UNKNOWN);
567
568 let default_unicast_locator = pick_routable_locator(
571 pl.find_all(pid::DEFAULT_UNICAST_LOCATOR)
572 .filter_map(|p| decode_locator(&p.value, little_endian)),
573 );
574
575 let default_multicast_locator = pl
576 .find(pid::DEFAULT_MULTICAST_LOCATOR)
577 .and_then(|p| decode_locator(&p.value, little_endian));
578
579 let metatraffic_unicast_locator = pick_routable_locator(
580 pl.find_all(pid::METATRAFFIC_UNICAST_LOCATOR)
581 .filter_map(|p| decode_locator(&p.value, little_endian)),
582 );
583
584 let metatraffic_multicast_locator = pl
585 .find(pid::METATRAFFIC_MULTICAST_LOCATOR)
586 .and_then(|p| decode_locator(&p.value, little_endian));
587
588 let domain_id = pl.find(pid::DOMAIN_ID).and_then(|p| {
589 if p.value.len() == 4 {
590 let mut bs = [0u8; 4];
591 bs.copy_from_slice(&p.value);
592 Some(if little_endian {
593 u32::from_le_bytes(bs)
594 } else {
595 u32::from_be_bytes(bs)
596 })
597 } else {
598 None
599 }
600 });
601
602 let builtin_endpoint_set = pl
603 .find(pid::BUILTIN_ENDPOINT_SET)
604 .and_then(|p| {
605 if p.value.len() == 4 {
606 let mut bs = [0u8; 4];
607 bs.copy_from_slice(&p.value);
608 Some(if little_endian {
609 u32::from_le_bytes(bs)
610 } else {
611 u32::from_be_bytes(bs)
612 })
613 } else {
614 None
615 }
616 })
617 .unwrap_or(0);
618
619 let lease_duration = pl
620 .find(pid::PARTICIPANT_LEASE_DURATION)
621 .and_then(|p| {
622 if p.value.len() == 8 {
623 let mut bs = [0u8; 8];
624 bs.copy_from_slice(&p.value);
625 Some(Duration::from_bytes_le(bs))
626 } else {
627 None
628 }
629 })
630 .unwrap_or(Duration::from_secs(100));
631
632 let user_data = pl
633 .find(pid::USER_DATA)
634 .and_then(|p| crate::publication_data::decode_octet_seq(&p.value, little_endian))
635 .unwrap_or_default();
636
637 let properties = pl
643 .find(pid::PROPERTY_LIST)
644 .and_then(|p| WirePropertyList::decode(&p.value, little_endian).ok())
645 .unwrap_or_default();
646
647 let identity_token = pl.find(pid::IDENTITY_TOKEN).map(|p| p.value.clone());
651 let permissions_token = pl.find(pid::PERMISSIONS_TOKEN).map(|p| p.value.clone());
652 let identity_status_token = pl.find(pid::IDENTITY_STATUS_TOKEN).map(|p| p.value.clone());
653 let participant_security_info = pl.find(pid::PARTICIPANT_SECURITY_INFO).and_then(|p| {
655 use crate::participant_security_info::ParticipantSecurityInfo;
656 if little_endian {
657 ParticipantSecurityInfo::from_le_bytes(&p.value).ok()
658 } else {
659 ParticipantSecurityInfo::from_be_bytes(&p.value).ok()
660 }
661 });
662
663 let sig_algo_info = pl
667 .find(pid::PARTICIPANT_SECURITY_DIGITAL_SIGNATURE_ALGORITHM_INFO)
668 .and_then(|p| {
669 crate::security_algo_info::ParticipantSecurityDigitalSignatureAlgorithmInfo::from_bytes(
670 &p.value,
671 little_endian,
672 )
673 .ok()
674 });
675 let kx_algo_info = pl
676 .find(pid::PARTICIPANT_SECURITY_KEY_ESTABLISHMENT_ALGORITHM_INFO)
677 .and_then(|p| {
678 crate::security_algo_info::ParticipantSecurityKeyEstablishmentAlgorithmInfo::from_bytes(
679 &p.value,
680 little_endian,
681 )
682 .ok()
683 });
684 let sym_cipher_algo_info = pl
685 .find(pid::PARTICIPANT_SECURITY_SYMMETRIC_CIPHER_ALGORITHM_INFO)
686 .and_then(|p| {
687 crate::security_algo_info::ParticipantSecuritySymmetricCipherAlgorithmInfo::from_bytes(
688 &p.value,
689 little_endian,
690 )
691 .ok()
692 });
693
694 Ok(Self {
695 guid,
696 protocol_version,
697 vendor_id,
698 default_unicast_locator,
699 default_multicast_locator,
700 metatraffic_unicast_locator,
701 metatraffic_multicast_locator,
702 domain_id,
703 builtin_endpoint_set,
704 lease_duration,
705 user_data,
706 properties,
707 identity_token,
708 permissions_token,
709 identity_status_token,
710 sig_algo_info,
711 kx_algo_info,
712 sym_cipher_algo_info,
713 participant_security_info,
714 })
715 }
716}
717
718fn locator_looks_routable(loc: &Locator) -> bool {
723 if loc.kind != LocatorKind::UdpV4 {
724 return true;
725 }
726 let ip = loc.ipv4();
727 let unspecified = ip == [0, 0, 0, 0];
728 let link_local = ip[0] == 169 && ip[1] == 254;
729 !(unspecified || link_local)
730}
731
732fn pick_routable_locator(candidates: impl Iterator<Item = Locator>) -> Option<Locator> {
739 let mut first = None;
740 let mut best = None;
741 for loc in candidates {
742 if first.is_none() {
743 first = Some(loc);
744 }
745 if best.is_none() && locator_looks_routable(&loc) {
746 best = Some(loc);
747 }
748 }
749 best.or(first)
750}
751
752fn decode_locator(value: &[u8], little_endian: bool) -> Option<Locator> {
753 if value.len() != Locator::WIRE_SIZE {
754 return None;
755 }
756 if !little_endian {
757 return None;
759 }
760 let mut bs = [0u8; 24];
761 bs.copy_from_slice(value);
762 Locator::from_bytes_le(bs).ok()
763}
764
765#[cfg(test)]
766mod tests {
767 #![allow(clippy::expect_used, clippy::unwrap_used)]
768 use super::*;
769 use crate::wire_types::{EntityId, GuidPrefix};
770 use alloc::vec;
771
772 #[test]
773 fn pick_routable_prefers_non_link_local() {
774 let link_local = Locator::udp_v4([169, 254, 1, 5], 7410);
779 let routable = Locator::udp_v4([192, 168, 1, 10], 7410);
780 assert_eq!(
781 pick_routable_locator([link_local, routable].into_iter()),
782 Some(routable)
783 );
784 assert_eq!(
786 pick_routable_locator([link_local].into_iter()),
787 Some(link_local)
788 );
789 let unspec = Locator::udp_v4([0, 0, 0, 0], 7410);
791 assert_eq!(
792 pick_routable_locator([unspec, routable].into_iter()),
793 Some(routable)
794 );
795 let loopback = Locator::udp_v4([127, 0, 0, 1], 7410);
797 assert_eq!(
798 pick_routable_locator([loopback].into_iter()),
799 Some(loopback)
800 );
801 assert_eq!(pick_routable_locator(core::iter::empty()), None);
802 }
803
804 fn sample_data() -> ParticipantBuiltinTopicData {
805 ParticipantBuiltinTopicData {
806 guid: Guid::new(
807 GuidPrefix::from_bytes([0xA, 0xB, 0xC, 0xD, 1, 2, 3, 4, 5, 6, 7, 8]),
808 EntityId::PARTICIPANT,
809 ),
810 protocol_version: ProtocolVersion::V2_5,
811 vendor_id: VendorId::ZERODDS,
812 default_unicast_locator: Some(Locator::udp_v4([192, 168, 1, 100], 7410)),
813 default_multicast_locator: Some(Locator::udp_v4([239, 255, 0, 1], 7400)),
814 metatraffic_unicast_locator: None,
815 metatraffic_multicast_locator: None,
816 domain_id: None,
817 builtin_endpoint_set: endpoint_flag::PARTICIPANT_ANNOUNCER
818 | endpoint_flag::PARTICIPANT_DETECTOR,
819 lease_duration: Duration::from_secs(100),
820 user_data: alloc::vec::Vec::new(),
821 properties: Default::default(),
822 identity_token: None,
823 permissions_token: None,
824 identity_status_token: None,
825 sig_algo_info: None,
826 kx_algo_info: None,
827 sym_cipher_algo_info: None,
828 participant_security_info: None,
829 }
830 }
831
832 #[test]
833 fn to_pl_cdr_be_is_big_endian_roundtrips_and_omits_credential_tokens() {
834 use crate::participant_security_info::{ParticipantSecurityInfo, attrs, plugin_attrs};
835 let mut d = sample_data();
836 d.participant_security_info = Some(ParticipantSecurityInfo {
837 participant_security_attributes: attrs::IS_VALID,
838 plugin_participant_security_attributes: plugin_attrs::IS_VALID,
839 });
840 d.identity_token = Some(alloc::vec![0xAB; 32]);
842 d.permissions_token = Some(alloc::vec![0xCD; 16]);
843 let be = d.to_pl_cdr_be();
844 assert_eq!(
846 &be[..4],
847 &[0x00, 0x02, 0x00, 0x00],
848 "c.pdata must be PL_CDR_BE"
849 );
850 let back = ParticipantBuiltinTopicData::from_pl_cdr_le(&be).unwrap();
852 assert_eq!(back.guid, d.guid);
853 assert_eq!(back.builtin_endpoint_set, d.builtin_endpoint_set);
854 assert_eq!(back.participant_security_info, d.participant_security_info);
855 assert!(
857 back.identity_token.is_none(),
858 "identity_token does not belong in c.pdata"
859 );
860 assert!(
861 back.permissions_token.is_none(),
862 "permissions_token does not belong in c.pdata"
863 );
864 }
865
866 #[test]
867 fn participant_security_info_pid_roundtrip() {
868 use crate::participant_security_info::{ParticipantSecurityInfo, attrs, plugin_attrs};
872 let mut d = sample_data();
873 d.participant_security_info = Some(ParticipantSecurityInfo {
874 participant_security_attributes: attrs::IS_VALID,
875 plugin_participant_security_attributes: plugin_attrs::IS_VALID,
876 });
877 let bytes = d.to_pl_cdr_le();
878 let has_pid = bytes.windows(2).any(|w| w == [0x05, 0x10]);
880 assert!(
881 has_pid,
882 "PID_PARTICIPANT_SECURITY_INFO missing from PL_CDR_LE"
883 );
884 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
885 assert_eq!(
886 decoded.participant_security_info,
887 d.participant_security_info
888 );
889 assert!(decoded.participant_security_info.unwrap().is_valid());
890 }
891
892 #[test]
893 fn duration_roundtrip_le() {
894 let d = Duration {
895 seconds: 30,
896 fraction: 500_000_000,
897 };
898 assert_eq!(Duration::from_bytes_le(d.to_bytes_le()), d);
899 }
900
901 #[test]
902 fn participant_data_roundtrip_full() {
903 let d = sample_data();
904 let bytes = d.to_pl_cdr_le();
905 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
906 assert_eq!(decoded, d);
907 }
908
909 #[test]
910 fn participant_data_first_4_bytes_are_pl_cdr_le_encapsulation() {
911 let d = sample_data();
912 let bytes = d.to_pl_cdr_le();
913 assert_eq!(&bytes[..4], &[0x00, 0x03, 0x00, 0x00]);
914 }
915
916 #[test]
917 fn participant_data_properties_roundtrip() {
918 use crate::property_list::WireProperty;
919 let mut d = sample_data();
920 d.properties.push(WireProperty::new(
921 "dds.sec.auth.plugin_class",
922 "DDS:Auth:PKI-DH:1.2",
923 ));
924 d.properties.push(WireProperty::new(
925 "zerodds.sec.offered_protection",
926 "ENCRYPT",
927 ));
928 let bytes = d.to_pl_cdr_le();
929 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
930 assert_eq!(decoded.properties, d.properties);
931 assert_eq!(
932 decoded.properties.get("zerodds.sec.offered_protection"),
933 Some("ENCRYPT")
934 );
935 }
936
937 #[test]
938 fn participant_data_empty_properties_omits_pid() {
939 let d = sample_data();
943 assert!(d.properties.is_empty());
944 let bytes = d.to_pl_cdr_le();
945 let has_pid = bytes.windows(2).any(|w| w == [0x59, 0x00]);
948 assert!(!has_pid, "empty properties must omit the PID");
949 }
950
951 #[test]
952 fn participant_data_legacy_peer_without_properties_parses_ok() {
953 let d = sample_data();
957 let bytes = d.to_pl_cdr_le();
958 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
959 assert!(decoded.properties.is_empty());
960 }
961
962 #[test]
963 fn participant_data_identity_token_pid_roundtrip() {
964 let mut d = sample_data();
968 d.identity_token = Some(vec![0xCA, 0xFE, 0xBA, 0xBE, 0x01, 0x02, 0x03, 0x04]);
975 let bytes = d.to_pl_cdr_le();
976 let has_pid = bytes.windows(2).any(|w| w == [0x01, 0x10]);
978 assert!(
979 has_pid,
980 "PID_IDENTITY_TOKEN missing from the PL_CDR_LE stream"
981 );
982 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
983 assert_eq!(decoded.identity_token, d.identity_token);
984 }
985
986 #[test]
987 fn participant_data_permissions_token_pid_roundtrip() {
988 let mut d = sample_data();
989 d.permissions_token = Some(vec![0xDE, 0xAD, 0xBE, 0xEF]);
990 let bytes = d.to_pl_cdr_le();
991 let has_pid = bytes.windows(2).any(|w| w == [0x02, 0x10]);
992 assert!(has_pid, "PID_PERMISSIONS_TOKEN missing");
993 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
994 assert_eq!(decoded.permissions_token, d.permissions_token);
995 }
996
997 #[test]
998 fn participant_data_identity_status_token_pid_roundtrip() {
999 let mut d = sample_data();
1000 d.identity_status_token = Some(vec![0x77, 0x88, 0x99, 0xAA]);
1001 let bytes = d.to_pl_cdr_le();
1002 let has_pid = bytes.windows(2).any(|w| w == [0x06, 0x10]);
1003 assert!(has_pid, "PID_IDENTITY_STATUS_TOKEN missing");
1004 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
1005 assert_eq!(decoded.identity_status_token, d.identity_status_token);
1006 }
1007
1008 #[test]
1009 fn participant_data_no_token_pids_in_legacy_announce() {
1010 let d = sample_data();
1013 let bytes = d.to_pl_cdr_le();
1014 for pid_le in [[0x01u8, 0x10], [0x02, 0x10], [0x06, 0x10]] {
1015 let found = bytes.windows(2).any(|w| w == pid_le);
1016 assert!(!found, "token PID {pid_le:?} must not appear in legacy");
1017 }
1018 }
1019
1020 #[test]
1021 fn participant_data_three_tokens_combined_roundtrip() {
1022 let mut d = sample_data();
1024 d.identity_token = Some(vec![0x01; 64]);
1025 d.permissions_token = Some(vec![0x02; 32]);
1026 d.identity_status_token = Some(vec![0x03; 16]);
1027 let bytes = d.to_pl_cdr_le();
1028 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
1029 assert_eq!(decoded, d);
1030 }
1031
1032 #[test]
1035 fn participant_data_sig_algo_info_roundtrip() {
1036 let mut d = sample_data();
1037 d.sig_algo_info =
1038 Some(crate::security_algo_info::ParticipantSecurityDigitalSignatureAlgorithmInfo::spec_default());
1039 let bytes = d.to_pl_cdr_le();
1040 assert!(
1042 bytes.windows(2).any(|w| w == [0x10, 0x10]),
1043 "PID 0x1010 missing from the PL_CDR_LE stream"
1044 );
1045 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
1046 assert_eq!(decoded.sig_algo_info, d.sig_algo_info);
1047 }
1048
1049 #[test]
1050 fn participant_data_kx_algo_info_roundtrip() {
1051 let mut d = sample_data();
1052 d.kx_algo_info =
1053 Some(crate::security_algo_info::ParticipantSecurityKeyEstablishmentAlgorithmInfo::spec_default());
1054 let bytes = d.to_pl_cdr_le();
1055 assert!(
1056 bytes.windows(2).any(|w| w == [0x11, 0x10]),
1057 "PID 0x1011 missing"
1058 );
1059 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
1060 assert_eq!(decoded.kx_algo_info, d.kx_algo_info);
1061 }
1062
1063 #[test]
1064 fn participant_data_sym_cipher_algo_info_roundtrip() {
1065 let mut d = sample_data();
1066 d.sym_cipher_algo_info =
1067 Some(crate::security_algo_info::ParticipantSecuritySymmetricCipherAlgorithmInfo::spec_default());
1068 let bytes = d.to_pl_cdr_le();
1069 assert!(
1070 bytes.windows(2).any(|w| w == [0x12, 0x10]),
1071 "PID 0x1012 missing"
1072 );
1073 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
1074 assert_eq!(decoded.sym_cipher_algo_info, d.sym_cipher_algo_info);
1075 }
1076
1077 #[test]
1078 fn participant_data_no_algo_info_in_legacy_announce() {
1079 let d = sample_data();
1081 let bytes = d.to_pl_cdr_le();
1082 for pid_le in [[0x10u8, 0x10], [0x11, 0x10], [0x12, 0x10]] {
1083 assert!(
1084 !bytes.windows(2).any(|w| w == pid_le),
1085 "algo PID {pid_le:?} must not appear in legacy"
1086 );
1087 }
1088 }
1089
1090 #[test]
1091 fn participant_data_all_three_algo_infos_combined() {
1092 let mut d = sample_data();
1093 d.sig_algo_info =
1094 Some(crate::security_algo_info::ParticipantSecurityDigitalSignatureAlgorithmInfo::spec_default());
1095 d.kx_algo_info =
1096 Some(crate::security_algo_info::ParticipantSecurityKeyEstablishmentAlgorithmInfo::spec_default());
1097 d.sym_cipher_algo_info =
1098 Some(crate::security_algo_info::ParticipantSecuritySymmetricCipherAlgorithmInfo::spec_default());
1099 let bytes = d.to_pl_cdr_le();
1100 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
1101 assert_eq!(decoded, d);
1102 }
1103
1104 #[test]
1105 fn participant_data_without_optional_locators() {
1106 let mut d = sample_data();
1107 d.default_unicast_locator = None;
1108 d.default_multicast_locator = None;
1109 let bytes = d.to_pl_cdr_le();
1110 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
1111 assert!(decoded.default_unicast_locator.is_none());
1112 assert!(decoded.default_multicast_locator.is_none());
1113 }
1114
1115 #[test]
1116 fn participant_data_decode_rejects_unknown_encapsulation() {
1117 let mut bytes = vec![0x99, 0x99, 0, 0]; bytes.extend_from_slice(&[0x01, 0x00, 0x00, 0x00]); let res = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes);
1120 assert!(matches!(
1121 res,
1122 Err(WireError::UnsupportedEncapsulation { kind: [0x99, 0x99] })
1123 ));
1124 }
1125
1126 #[test]
1127 fn participant_data_decode_requires_guid_pid() {
1128 let bytes = vec![0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
1130 let res = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes);
1131 assert!(matches!(res, Err(WireError::ValueOutOfRange { .. })));
1132 }
1133
1134 #[test]
1135 fn endpoint_flags_have_distinct_bits() {
1136 let flags = [
1138 endpoint_flag::PARTICIPANT_ANNOUNCER,
1139 endpoint_flag::PARTICIPANT_DETECTOR,
1140 endpoint_flag::PUBLICATIONS_ANNOUNCER,
1141 endpoint_flag::PUBLICATIONS_DETECTOR,
1142 endpoint_flag::SUBSCRIPTIONS_ANNOUNCER,
1143 endpoint_flag::SUBSCRIPTIONS_DETECTOR,
1144 endpoint_flag::PARTICIPANT_MESSAGE_DATA_WRITER,
1145 endpoint_flag::PARTICIPANT_MESSAGE_DATA_READER,
1146 endpoint_flag::PUBLICATIONS_SECURE_WRITER,
1147 endpoint_flag::PUBLICATIONS_SECURE_READER,
1148 endpoint_flag::SUBSCRIPTIONS_SECURE_WRITER,
1149 endpoint_flag::SUBSCRIPTIONS_SECURE_READER,
1150 endpoint_flag::PARTICIPANT_MESSAGE_SECURE_WRITER,
1151 endpoint_flag::PARTICIPANT_MESSAGE_SECURE_READER,
1152 endpoint_flag::PARTICIPANT_STATELESS_MESSAGE_WRITER,
1153 endpoint_flag::PARTICIPANT_STATELESS_MESSAGE_READER,
1154 endpoint_flag::PARTICIPANT_VOLATILE_MESSAGE_SECURE_WRITER,
1155 endpoint_flag::PARTICIPANT_VOLATILE_MESSAGE_SECURE_READER,
1156 endpoint_flag::PARTICIPANT_SECURE_WRITER,
1157 endpoint_flag::PARTICIPANT_SECURE_READER,
1158 endpoint_flag::TOPICS_ANNOUNCER,
1159 endpoint_flag::TOPICS_DETECTOR,
1160 ];
1161 for (i, &a) in flags.iter().enumerate() {
1162 for &b in &flags[i + 1..] {
1163 assert_eq!(a & b, 0, "flag bits must be distinct");
1164 }
1165 }
1166 }
1167
1168 #[test]
1169 fn endpoint_flag_bit_positions_match_spec() {
1170 assert_eq!(endpoint_flag::PARTICIPANT_ANNOUNCER, 0x0000_0001);
1175 assert_eq!(endpoint_flag::PARTICIPANT_DETECTOR, 0x0000_0002);
1176 assert_eq!(endpoint_flag::PUBLICATIONS_ANNOUNCER, 0x0000_0004);
1177 assert_eq!(endpoint_flag::PUBLICATIONS_DETECTOR, 0x0000_0008);
1178 assert_eq!(endpoint_flag::SUBSCRIPTIONS_ANNOUNCER, 0x0000_0010);
1179 assert_eq!(endpoint_flag::SUBSCRIPTIONS_DETECTOR, 0x0000_0020);
1180 assert_eq!(endpoint_flag::PARTICIPANT_MESSAGE_DATA_WRITER, 0x0000_0400);
1181 assert_eq!(endpoint_flag::PARTICIPANT_MESSAGE_DATA_READER, 0x0000_0800);
1182 assert_eq!(endpoint_flag::PUBLICATIONS_SECURE_WRITER, 0x0001_0000);
1183 assert_eq!(endpoint_flag::PUBLICATIONS_SECURE_READER, 0x0002_0000);
1184 assert_eq!(endpoint_flag::SUBSCRIPTIONS_SECURE_WRITER, 0x0004_0000);
1185 assert_eq!(endpoint_flag::SUBSCRIPTIONS_SECURE_READER, 0x0008_0000);
1186 assert_eq!(
1187 endpoint_flag::PARTICIPANT_MESSAGE_SECURE_WRITER,
1188 0x0010_0000
1189 );
1190 assert_eq!(
1191 endpoint_flag::PARTICIPANT_MESSAGE_SECURE_READER,
1192 0x0020_0000
1193 );
1194 assert_eq!(
1195 endpoint_flag::PARTICIPANT_STATELESS_MESSAGE_WRITER,
1196 0x0040_0000
1197 );
1198 assert_eq!(
1199 endpoint_flag::PARTICIPANT_STATELESS_MESSAGE_READER,
1200 0x0080_0000
1201 );
1202 assert_eq!(
1203 endpoint_flag::PARTICIPANT_VOLATILE_MESSAGE_SECURE_WRITER,
1204 0x0100_0000
1205 );
1206 assert_eq!(
1207 endpoint_flag::PARTICIPANT_VOLATILE_MESSAGE_SECURE_READER,
1208 0x0200_0000
1209 );
1210 assert_eq!(endpoint_flag::PARTICIPANT_SECURE_WRITER, 0x0400_0000);
1211 assert_eq!(endpoint_flag::PARTICIPANT_SECURE_READER, 0x0800_0000);
1212 assert_eq!(endpoint_flag::TOPICS_ANNOUNCER, 0x1000_0000);
1213 assert_eq!(endpoint_flag::TOPICS_DETECTOR, 0x2000_0000);
1214 }
1215
1216 #[test]
1217 fn endpoint_flag_all_secure_covers_bits_16_to_27() {
1218 let mask = endpoint_flag::ALL_SECURE;
1222 for bit in 16u32..=27 {
1223 assert!(
1224 mask & (1u32 << bit) != 0,
1225 "bit {bit} missing from ALL_SECURE"
1226 );
1227 }
1228 let outside_mask: u32 = !((1u32 << 28) - (1u32 << 16));
1230 assert_eq!(
1231 mask & outside_mask,
1232 0,
1233 "ALL_SECURE may only set bits 16..27"
1234 );
1235 }
1236
1237 #[test]
1238 fn endpoint_flag_all_standard_excludes_secure_bits() {
1239 assert_eq!(endpoint_flag::ALL_STANDARD & endpoint_flag::ALL_SECURE, 0);
1243 }
1244
1245 #[test]
1246 fn endpoint_flag_roundtrip_through_pl_cdr() {
1247 let combined = endpoint_flag::ALL_STANDARD | endpoint_flag::ALL_SECURE;
1250 let mut d = sample_data();
1251 d.builtin_endpoint_set = combined;
1252 let bytes = d.to_pl_cdr_le();
1253 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
1254 assert_eq!(decoded.builtin_endpoint_set, combined);
1255 }
1256}