1use alloc::string::String;
18use alloc::vec::Vec;
19
20use zerodds_cdr::{BufferReader, BufferWriter, Endianness};
21use zerodds_corba_csiv2::CompoundSecMechList;
22use zerodds_corba_iiop::profile_body::CdrError;
23
24use crate::component_tags::ComponentId;
25
26#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct TaggedComponent {
29 pub tag: ComponentId,
31 pub component_data: Vec<u8>,
33}
34
35impl TaggedComponent {
36 pub fn encode(&self, w: &mut BufferWriter) -> Result<(), CdrError> {
41 w.write_u32(self.tag.as_u32())?;
42 let n = u32::try_from(self.component_data.len()).map_err(|_| CdrError::Overflow)?;
43 w.write_u32(n)?;
44 w.write_bytes(&self.component_data)?;
45 Ok(())
46 }
47
48 pub fn decode(r: &mut BufferReader<'_>) -> Result<Self, CdrError> {
53 let tag = ComponentId::from_u32(r.read_u32()?);
54 let n = r.read_u32()? as usize;
55 let bytes = r.read_bytes(n)?;
56 Ok(Self {
57 tag,
58 component_data: bytes.to_vec(),
59 })
60 }
61
62 pub fn structured(&self) -> Result<StructuredComponent, CdrError> {
67 StructuredComponent::decode(self.tag, &self.component_data)
68 }
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub struct OrbType(pub u32);
78
79#[derive(Debug, Clone, PartialEq, Eq)]
81pub struct CodeSetComponent {
82 pub native_code_set: u32,
84 pub conversion_code_sets: Vec<u32>,
87}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct CodeSetComponentInfo {
92 pub for_char_data: CodeSetComponent,
94 pub for_wchar_data: CodeSetComponent,
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct AlternateIiopAddress {
101 pub host: String,
103 pub port: u16,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct Ssl {
110 pub target_supports: u16,
112 pub target_requires: u16,
114 pub port: u16,
116}
117
118#[derive(Debug, Clone, PartialEq, Eq)]
121pub struct TlsSecTrans {
122 pub target_supports: u16,
124 pub target_requires: u16,
126 pub addresses: Vec<AlternateIiopAddress>,
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub struct StreamFormatVersion(pub u8);
133
134#[derive(Debug, Clone, PartialEq, Eq)]
136pub enum StructuredComponent {
137 OrbType(OrbType),
139 CodeSets(CodeSetComponentInfo),
141 AlternateIiopAddress(AlternateIiopAddress),
143 Ssl(Ssl),
145 TlsSecTrans(TlsSecTrans),
147 CsiSecMechList(CompoundSecMechList),
149 StreamFormatVersion(StreamFormatVersion),
151 JavaCodebase(String),
153 Opaque {
155 tag: ComponentId,
157 bytes: Vec<u8>,
159 },
160}
161
162impl StructuredComponent {
163 pub fn decode(tag: ComponentId, encap: &[u8]) -> Result<Self, CdrError> {
169 let endianness = read_endianness(encap)?;
170 let body = &encap[1..];
171 match tag {
172 ComponentId::OrbType => {
173 let mut r = BufferReader::new(body, endianness);
174 Ok(Self::OrbType(OrbType(r.read_u32()?)))
175 }
176 ComponentId::CodeSets => {
177 let mut r = BufferReader::new(body, endianness);
178 let for_char = decode_code_set_component(&mut r)?;
179 let for_wchar = decode_code_set_component(&mut r)?;
180 Ok(Self::CodeSets(CodeSetComponentInfo {
181 for_char_data: for_char,
182 for_wchar_data: for_wchar,
183 }))
184 }
185 ComponentId::AlternateIiopAddress => {
186 let mut r = BufferReader::new(body, endianness);
187 let host = r.read_string()?;
188 let port = r.read_u16()?;
189 Ok(Self::AlternateIiopAddress(AlternateIiopAddress {
190 host,
191 port,
192 }))
193 }
194 ComponentId::SslSecTrans => {
195 let mut r = BufferReader::new(body, endianness);
196 Ok(Self::Ssl(Ssl {
197 target_supports: r.read_u16()?,
198 target_requires: r.read_u16()?,
199 port: r.read_u16()?,
200 }))
201 }
202 ComponentId::TlsSecTrans => {
203 let mut r = BufferReader::new(body, endianness);
204 let target_supports = r.read_u16()?;
205 let target_requires = r.read_u16()?;
206 let n = r.read_u32()? as usize;
207 let mut addresses = Vec::with_capacity(n.min(32));
208 for _ in 0..n {
209 let host = r.read_string()?;
210 let port = r.read_u16()?;
211 addresses.push(AlternateIiopAddress { host, port });
212 }
213 Ok(Self::TlsSecTrans(TlsSecTrans {
214 target_supports,
215 target_requires,
216 addresses,
217 }))
218 }
219 ComponentId::CsiSecMechList => {
220 let mut r = BufferReader::new(body, endianness);
221 Ok(Self::CsiSecMechList(CompoundSecMechList::decode(&mut r)?))
222 }
223 ComponentId::RmiCustomMaxStreamFormat => {
224 let mut r = BufferReader::new(body, endianness);
225 Ok(Self::StreamFormatVersion(StreamFormatVersion(r.read_u8()?)))
226 }
227 ComponentId::JavaCodebase => {
228 let mut r = BufferReader::new(body, endianness);
229 Ok(Self::JavaCodebase(r.read_string()?))
230 }
231 other => Ok(Self::Opaque {
232 tag: other,
233 bytes: encap.to_vec(),
234 }),
235 }
236 }
237
238 pub fn encode_encapsulation(&self, endianness: Endianness) -> Result<Vec<u8>, CdrError> {
244 let mut out = Vec::with_capacity(64);
245 out.push(endianness_to_byte(endianness));
246 let mut w = BufferWriter::new(endianness);
247 match self {
248 Self::OrbType(OrbType(v)) => w.write_u32(*v)?,
249 Self::CodeSets(info) => {
250 encode_code_set_component(&mut w, &info.for_char_data)?;
251 encode_code_set_component(&mut w, &info.for_wchar_data)?;
252 }
253 Self::AlternateIiopAddress(a) => {
254 w.write_string(&a.host)?;
255 w.write_u16(a.port)?;
256 }
257 Self::Ssl(s) => {
258 w.write_u16(s.target_supports)?;
259 w.write_u16(s.target_requires)?;
260 w.write_u16(s.port)?;
261 }
262 Self::TlsSecTrans(t) => {
263 w.write_u16(t.target_supports)?;
264 w.write_u16(t.target_requires)?;
265 let n = u32::try_from(t.addresses.len()).map_err(|_| CdrError::Overflow)?;
266 w.write_u32(n)?;
267 for a in &t.addresses {
268 w.write_string(&a.host)?;
269 w.write_u16(a.port)?;
270 }
271 }
272 Self::CsiSecMechList(list) => list.encode(&mut w)?,
273 Self::StreamFormatVersion(StreamFormatVersion(v)) => w.write_u8(*v)?,
274 Self::JavaCodebase(s) => w.write_string(s)?,
275 Self::Opaque { bytes, .. } => {
276 return Ok(bytes.clone());
279 }
280 }
281 out.extend_from_slice(w.as_bytes());
282 Ok(out)
283 }
284}
285
286fn read_endianness(encap: &[u8]) -> Result<Endianness, CdrError> {
287 if encap.is_empty() {
288 return Err(CdrError::Truncated);
289 }
290 match encap[0] {
291 0 => Ok(Endianness::Big),
292 1 => Ok(Endianness::Little),
293 _ => Err(CdrError::InvalidEndianness),
294 }
295}
296
297const fn endianness_to_byte(e: Endianness) -> u8 {
298 match e {
299 Endianness::Big => 0,
300 Endianness::Little => 1,
301 }
302}
303
304fn decode_code_set_component(r: &mut BufferReader<'_>) -> Result<CodeSetComponent, CdrError> {
305 let native_code_set = r.read_u32()?;
306 let n = r.read_u32()? as usize;
307 let mut conversion = Vec::with_capacity(n.min(16));
308 for _ in 0..n {
309 conversion.push(r.read_u32()?);
310 }
311 Ok(CodeSetComponent {
312 native_code_set,
313 conversion_code_sets: conversion,
314 })
315}
316
317const CS_UTF_8: u32 = 0x0501_0001;
319const CS_UTF_16: u32 = 0x0001_0109;
320
321impl CodeSetComponent {
322 #[must_use]
334 pub fn negotiate(&self, server: &CodeSetComponent, fallback: u32) -> Option<u32> {
335 let cn = self.native_code_set;
336 let sn = server.native_code_set;
337 let supports = |c: &CodeSetComponent, id: u32| {
338 id != 0 && (c.native_code_set == id || c.conversion_code_sets.contains(&id))
339 };
340 if cn != 0 && cn == sn {
342 return Some(cn);
343 }
344 if sn != 0 && self.conversion_code_sets.contains(&sn) {
346 return Some(sn);
347 }
348 if cn != 0 && server.conversion_code_sets.contains(&cn) {
350 return Some(cn);
351 }
352 if let Some(common) = self
354 .conversion_code_sets
355 .iter()
356 .find(|id| server.conversion_code_sets.contains(id))
357 {
358 return Some(*common);
359 }
360 if supports(self, fallback) && supports(server, fallback) {
362 return Some(fallback);
363 }
364 None
365 }
366}
367
368impl CodeSetComponentInfo {
369 #[must_use]
374 pub fn negotiate(&self, server: &CodeSetComponentInfo) -> Option<(u32, u32)> {
375 let tcsc = self
376 .for_char_data
377 .negotiate(&server.for_char_data, CS_UTF_8)?;
378 let tcsw = self
379 .for_wchar_data
380 .negotiate(&server.for_wchar_data, CS_UTF_16)?;
381 Some((tcsc, tcsw))
382 }
383}
384
385fn encode_code_set_component(w: &mut BufferWriter, c: &CodeSetComponent) -> Result<(), CdrError> {
386 w.write_u32(c.native_code_set)?;
387 let n = u32::try_from(c.conversion_code_sets.len()).map_err(|_| CdrError::Overflow)?;
388 w.write_u32(n)?;
389 for cs in &c.conversion_code_sets {
390 w.write_u32(*cs)?;
391 }
392 Ok(())
393}
394
395#[cfg(test)]
396#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
397mod tests {
398 use super::*;
399
400 #[test]
401 fn orb_type_round_trip() {
402 let s = StructuredComponent::OrbType(OrbType(0x4F4D_4732)); let bytes = s.encode_encapsulation(Endianness::Big).unwrap();
404 let decoded = StructuredComponent::decode(ComponentId::OrbType, &bytes).unwrap();
405 assert_eq!(decoded, s);
406 }
407
408 #[test]
409 fn code_sets_round_trip_le() {
410 let info = CodeSetComponentInfo {
411 for_char_data: CodeSetComponent {
412 native_code_set: 0x0001_0001,
413 conversion_code_sets: alloc::vec![0x0001_0109],
414 },
415 for_wchar_data: CodeSetComponent {
416 native_code_set: 0x0001_0109,
417 conversion_code_sets: alloc::vec![],
418 },
419 };
420 let s = StructuredComponent::CodeSets(info.clone());
421 let bytes = s.encode_encapsulation(Endianness::Little).unwrap();
422 let decoded = StructuredComponent::decode(ComponentId::CodeSets, &bytes).unwrap();
423 match decoded {
424 StructuredComponent::CodeSets(d) => assert_eq!(d, info),
425 other => panic!("expected CodeSets, got {other:?}"),
426 }
427 }
428
429 fn cs(native: u32, conv: &[u32]) -> CodeSetComponent {
430 CodeSetComponent {
431 native_code_set: native,
432 conversion_code_sets: conv.to_vec(),
433 }
434 }
435
436 #[test]
437 fn negotiate_native_match() {
438 let client = cs(0x0001_0001, &[]);
440 let server = cs(0x0001_0001, &[]);
441 assert_eq!(client.negotiate(&server, CS_UTF_8), Some(0x0001_0001));
442 }
443
444 #[test]
445 fn negotiate_client_converts_to_server_native() {
446 let client = cs(0x0001_0001, &[CS_UTF_8]);
448 let server = cs(CS_UTF_8, &[]);
449 assert_eq!(client.negotiate(&server, CS_UTF_8), Some(CS_UTF_8));
450 }
451
452 #[test]
453 fn negotiate_server_converts_to_client_native() {
454 let client = cs(0x0001_0001, &[]);
456 let server = cs(CS_UTF_8, &[0x0001_0001]);
457 assert_eq!(client.negotiate(&server, CS_UTF_8), Some(0x0001_0001));
458 }
459
460 #[test]
461 fn negotiate_common_conversion_set() {
462 let client = cs(0x0001_0002, &[CS_UTF_8]);
464 let server = cs(0x0001_0003, &[CS_UTF_8]);
465 assert_eq!(client.negotiate(&server, 0xDEAD), Some(CS_UTF_8));
466 }
467
468 #[test]
469 fn negotiate_universal_fallback() {
470 let client = cs(CS_UTF_16, &[]);
472 let server = cs(0x0001_0100, &[CS_UTF_16]);
473 assert_eq!(client.negotiate(&server, CS_UTF_16), Some(CS_UTF_16));
474 }
475
476 #[test]
477 fn negotiate_incompatible_yields_none() {
478 let client = cs(0x0001_0002, &[0x0001_0004]);
479 let server = cs(0x0001_0003, &[0x0001_0005]);
480 assert_eq!(client.negotiate(&server, 0xBEEF), None);
481 }
482
483 #[test]
484 fn negotiate_info_both_axes_default_fallbacks() {
485 let client = CodeSetComponentInfo {
487 for_char_data: cs(0x0001_0001, &[CS_UTF_8]),
488 for_wchar_data: cs(CS_UTF_16, &[]),
489 };
490 let server = CodeSetComponentInfo {
491 for_char_data: cs(CS_UTF_8, &[]),
492 for_wchar_data: cs(CS_UTF_16, &[]),
493 };
494 assert_eq!(client.negotiate(&server), Some((CS_UTF_8, CS_UTF_16)));
495 }
496
497 #[test]
498 fn negotiate_info_none_if_one_axis_fails() {
499 let client = CodeSetComponentInfo {
500 for_char_data: cs(CS_UTF_8, &[]),
501 for_wchar_data: cs(0x0001_0002, &[]), };
503 let server = CodeSetComponentInfo {
504 for_char_data: cs(CS_UTF_8, &[]),
505 for_wchar_data: cs(0x0001_0003, &[]),
506 };
507 assert_eq!(client.negotiate(&server), None);
508 }
509
510 #[test]
511 fn alternate_iiop_address_round_trip() {
512 let s = StructuredComponent::AlternateIiopAddress(AlternateIiopAddress {
513 host: "alt.host".into(),
514 port: 1234,
515 });
516 let bytes = s.encode_encapsulation(Endianness::Big).unwrap();
517 let decoded =
518 StructuredComponent::decode(ComponentId::AlternateIiopAddress, &bytes).unwrap();
519 assert_eq!(decoded, s);
520 }
521
522 #[test]
523 fn ssl_sec_trans_round_trip() {
524 let s = StructuredComponent::Ssl(Ssl {
525 target_supports: 0x0040,
526 target_requires: 0x0020,
527 port: 4242,
528 });
529 let bytes = s.encode_encapsulation(Endianness::Big).unwrap();
530 let decoded = StructuredComponent::decode(ComponentId::SslSecTrans, &bytes).unwrap();
531 assert_eq!(decoded, s);
532 }
533
534 #[test]
535 fn tls_sec_trans_with_addresses_round_trip() {
536 let s = StructuredComponent::TlsSecTrans(TlsSecTrans {
537 target_supports: 0x0040,
538 target_requires: 0x0040,
539 addresses: alloc::vec![
540 AlternateIiopAddress {
541 host: "tls-a.lab".into(),
542 port: 443,
543 },
544 AlternateIiopAddress {
545 host: "tls-b.lab".into(),
546 port: 8443,
547 },
548 ],
549 });
550 let bytes = s.encode_encapsulation(Endianness::Little).unwrap();
551 let decoded = StructuredComponent::decode(ComponentId::TlsSecTrans, &bytes).unwrap();
552 assert_eq!(decoded, s);
553 }
554
555 #[test]
556 fn csi_sec_mech_list_round_trip() {
557 use zerodds_corba_csiv2::{
558 AsContextSec, AssociationOptions, CompoundSecMech, CompoundSecMechList, SasContextSec,
559 };
560 let list = CompoundSecMechList {
561 stateful: true,
562 mechanism_list: alloc::vec![CompoundSecMech {
563 target_requires: AssociationOptions(
564 AssociationOptions::INTEGRITY | AssociationOptions::CONFIDENTIALITY,
565 ),
566 transport_mech_tag: 36, transport_mech_data: alloc::vec![0x01, 0x02, 0x03],
568 as_context: AsContextSec {
569 target_supports: AssociationOptions(0x0040),
570 target_requires: AssociationOptions(0x0040),
571 client_authentication_mech: alloc::vec![0xAA, 0xBB],
572 target_name: alloc::vec![0xCC],
573 },
574 sas_context: SasContextSec {
575 target_supports: AssociationOptions(0x0080),
576 target_requires: AssociationOptions(0x0080),
577 privilege_authorities: alloc::vec![alloc::vec![0xDE, 0xAD]],
578 supported_naming_mechanisms: alloc::vec![alloc::vec![0xBE, 0xEF]],
579 supported_identity_types: 0x0001_0203,
580 },
581 }],
582 };
583 let s = StructuredComponent::CsiSecMechList(list.clone());
584 let bytes = s.encode_encapsulation(Endianness::Little).unwrap();
585 let decoded = StructuredComponent::decode(ComponentId::CsiSecMechList, &bytes).unwrap();
586 match decoded {
587 StructuredComponent::CsiSecMechList(d) => assert_eq!(d, list),
588 other => panic!("expected CsiSecMechList, got {other:?}"),
589 }
590 }
591
592 #[test]
593 fn stream_format_version_round_trip() {
594 let s = StructuredComponent::StreamFormatVersion(StreamFormatVersion(2));
595 let bytes = s.encode_encapsulation(Endianness::Big).unwrap();
596 let decoded =
597 StructuredComponent::decode(ComponentId::RmiCustomMaxStreamFormat, &bytes).unwrap();
598 assert_eq!(decoded, s);
599 }
600
601 #[test]
602 fn java_codebase_round_trip() {
603 let s = StructuredComponent::JavaCodebase("http://server/codebase.jar".into());
604 let bytes = s.encode_encapsulation(Endianness::Big).unwrap();
605 let decoded = StructuredComponent::decode(ComponentId::JavaCodebase, &bytes).unwrap();
606 assert_eq!(decoded, s);
607 }
608
609 #[test]
610 fn opaque_unknown_tag_pass_through() {
611 let raw = alloc::vec![1, 0xff, 0xee, 0xdd];
612 let s = StructuredComponent::decode(ComponentId::Other(9999), &raw).unwrap();
613 match s {
614 StructuredComponent::Opaque { tag, bytes } => {
615 assert_eq!(tag, ComponentId::Other(9999));
616 assert_eq!(bytes, raw);
617 }
618 other => panic!("expected Opaque, got {other:?}"),
619 }
620 }
621
622 #[test]
623 fn invalid_endianness_byte_is_diagnostic() {
624 let bytes = alloc::vec![0xff, 0, 0, 0, 1];
625 let err = StructuredComponent::decode(ComponentId::OrbType, &bytes).unwrap_err();
626 assert!(matches!(err, CdrError::InvalidEndianness));
627 }
628
629 #[test]
630 fn tagged_component_round_trip() {
631 let s = StructuredComponent::OrbType(OrbType(42));
632 let bytes = s.encode_encapsulation(Endianness::Big).unwrap();
633 let tc = TaggedComponent {
634 tag: ComponentId::OrbType,
635 component_data: bytes,
636 };
637 let mut w = BufferWriter::new(Endianness::Big);
638 tc.encode(&mut w).unwrap();
639 let buf = w.into_bytes();
640 let mut r = BufferReader::new(&buf, Endianness::Big);
641 let decoded = TaggedComponent::decode(&mut r).unwrap();
642 assert_eq!(decoded, tc);
643 }
644}