1use crate::error::{Error, Result};
37use crate::objects;
38use crate::tag::ApduTag;
39use alloc::vec::Vec;
40use broadcast_common::{Parse, Serialize};
41
42pub mod tag {
44 use crate::tag::ApduTag;
45 pub const COMMS_INFO_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x8C, 0x07);
47 pub const COMMS_INFO_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0x8C, 0x08);
49 pub const COMMS_IP_CONFIG_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x8C, 0x09);
51 pub const COMMS_IP_CONFIG_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0x8C, 0x0A);
53}
54
55pub const IP_ADDR_LEN: usize = 16;
57pub const MAC_LEN: usize = 6;
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65#[cfg_attr(feature = "serde", derive(serde::Serialize))]
66#[non_exhaustive]
67pub enum ConnectionState {
68 Disconnected,
70 Connected,
72 Reserved(u8),
74}
75impl ConnectionState {
76 #[must_use]
78 pub fn from_u8(v: u8) -> Self {
79 match v & 0x03 {
80 0x00 => Self::Disconnected,
81 0x01 => Self::Connected,
82 other => Self::Reserved(other),
83 }
84 }
85 #[must_use]
87 pub const fn to_u8(self) -> u8 {
88 match self {
89 Self::Disconnected => 0x00,
90 Self::Connected => 0x01,
91 Self::Reserved(v) => v & 0x03,
92 }
93 }
94 #[must_use]
96 pub fn name(&self) -> &'static str {
97 match self {
98 Self::Disconnected => "disconnected",
99 Self::Connected => "connected",
100 Self::Reserved(_) => "reserved",
101 }
102 }
103}
104broadcast_common::impl_spec_display!(ConnectionState, Reserved);
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110#[cfg_attr(feature = "serde", derive(serde::Serialize))]
111#[non_exhaustive]
112pub enum IpProtocolVersion {
113 Ipv4,
115 Ipv6,
117 Reserved(u8),
119}
120impl IpProtocolVersion {
121 #[must_use]
123 pub fn from_u8(v: u8) -> Self {
124 match v {
125 0x01 => Self::Ipv4,
126 0x02 => Self::Ipv6,
127 other => Self::Reserved(other),
128 }
129 }
130 #[must_use]
132 pub const fn to_u8(self) -> u8 {
133 match self {
134 Self::Ipv4 => 0x01,
135 Self::Ipv6 => 0x02,
136 Self::Reserved(v) => v,
137 }
138 }
139 #[must_use]
141 pub fn name(&self) -> &'static str {
142 match self {
143 Self::Ipv4 => "ipv4",
144 Self::Ipv6 => "ipv6",
145 Self::Reserved(_) => "reserved",
146 }
147 }
148}
149broadcast_common::impl_spec_display!(IpProtocolVersion, Reserved);
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
160#[cfg_attr(feature = "serde", derive(serde::Serialize))]
161pub struct CommsInfoReq;
162
163impl<'a> Parse<'a> for CommsInfoReq {
164 type Error = Error;
165 fn parse(bytes: &'a [u8]) -> Result<Self> {
166 objects::parse_empty_apdu(bytes, tag::COMMS_INFO_REQ, "comms_info_req")?;
167 Ok(Self)
168 }
169}
170impl Serialize for CommsInfoReq {
171 type Error = Error;
172 fn serialized_len(&self) -> usize {
173 objects::empty_apdu_len()
174 }
175 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
176 objects::serialize_empty_apdu(tag::COMMS_INFO_REQ, buf)
177 }
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186#[cfg_attr(feature = "serde", derive(serde::Serialize))]
187pub struct CommsInfoReply {
188 pub lts_id: u8,
190 pub status: bool,
192 pub source_ip_address: [u8; IP_ADDR_LEN],
194 pub source_port: u16,
196 pub input_delivery_pid: u16,
199}
200
201const INFO_REPLY_BODY: usize = 1 + 1 + IP_ADDR_LEN + 2 + 2;
204const STATUS_BIT: u8 = 0x01;
205const INPUT_DELIVERY_PID_MASK: u16 = 0x1FFF;
206
207impl<'a> Parse<'a> for CommsInfoReply {
208 type Error = Error;
209 fn parse(bytes: &'a [u8]) -> Result<Self> {
210 let body = objects::parse_apdu_header(bytes, tag::COMMS_INFO_REPLY, "comms_info_reply")?;
211 if body.len() < INFO_REPLY_BODY {
212 return Err(Error::BufferTooShort {
213 need: INFO_REPLY_BODY,
214 have: body.len(),
215 what: "comms_info_reply",
216 });
217 }
218 let lts_id = body[0];
219 let status = body[1] & STATUS_BIT != 0;
220 let mut source_ip_address = [0u8; IP_ADDR_LEN];
221 source_ip_address.copy_from_slice(&body[2..2 + IP_ADDR_LEN]);
222 let p = 2 + IP_ADDR_LEN;
223 let source_port = u16::from_be_bytes([body[p], body[p + 1]]);
224 let input_delivery_pid =
225 u16::from_be_bytes([body[p + 2], body[p + 3]]) & INPUT_DELIVERY_PID_MASK;
226 Ok(Self {
227 lts_id,
228 status,
229 source_ip_address,
230 source_port,
231 input_delivery_pid,
232 })
233 }
234}
235impl Serialize for CommsInfoReply {
236 type Error = Error;
237 fn serialized_len(&self) -> usize {
238 objects::apdu_len(INFO_REPLY_BODY)
239 }
240 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
241 let pos = objects::write_apdu_header(tag::COMMS_INFO_REPLY, INFO_REPLY_BODY, buf)?;
242 buf[pos] = self.lts_id;
243 buf[pos + 1] = u8::from(self.status);
245 buf[pos + 2..pos + 2 + IP_ADDR_LEN].copy_from_slice(&self.source_ip_address);
246 let p = pos + 2 + IP_ADDR_LEN;
247 buf[p..p + 2].copy_from_slice(&self.source_port.to_be_bytes());
248 buf[p + 2..p + 4]
250 .copy_from_slice(&(self.input_delivery_pid & INPUT_DELIVERY_PID_MASK).to_be_bytes());
251 Ok(pos + INFO_REPLY_BODY)
252 }
253}
254
255#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
261#[cfg_attr(feature = "serde", derive(serde::Serialize))]
262pub struct CommsIpConfigReq;
263
264impl<'a> Parse<'a> for CommsIpConfigReq {
265 type Error = Error;
266 fn parse(bytes: &'a [u8]) -> Result<Self> {
267 objects::parse_empty_apdu(bytes, tag::COMMS_IP_CONFIG_REQ, "comms_IP_config_req")?;
268 Ok(Self)
269 }
270}
271impl Serialize for CommsIpConfigReq {
272 type Error = Error;
273 fn serialized_len(&self) -> usize {
274 objects::empty_apdu_len()
275 }
276 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
277 objects::serialize_empty_apdu(tag::COMMS_IP_CONFIG_REQ, buf)
278 }
279}
280
281#[derive(Debug, Clone, PartialEq, Eq)]
288#[cfg_attr(feature = "serde", derive(serde::Serialize))]
289pub struct IpConfig {
290 pub ip_address: [u8; IP_ADDR_LEN],
292 pub network_mask: [u8; IP_ADDR_LEN],
294 pub default_gateway: [u8; IP_ADDR_LEN],
296 pub dhcp_server_address: [u8; IP_ADDR_LEN],
298 pub dns_server_addresses: Vec<[u8; IP_ADDR_LEN]>,
300}
301
302#[derive(Debug, Clone, PartialEq, Eq)]
304#[cfg_attr(feature = "serde", derive(serde::Serialize))]
305pub struct CommsIpConfigReply {
306 pub connection_state: ConnectionState,
308 pub physical_address: [u8; MAC_LEN],
310 pub ip_config: Option<IpConfig>,
312}
313
314const IP_CONFIG_PREFIX: usize = 1 + MAC_LEN;
316const IP_CONFIG_FIXED: usize = 4 * IP_ADDR_LEN + 1;
318const CONNECTION_STATE_CONNECTED: u8 = 0x01;
319
320impl CommsIpConfigReply {
321 fn body_len(&self) -> usize {
322 IP_CONFIG_PREFIX
323 + match &self.ip_config {
324 Some(c) => IP_CONFIG_FIXED + c.dns_server_addresses.len() * IP_ADDR_LEN,
325 None => 0,
326 }
327 }
328}
329
330fn read_addr(body: &[u8], pos: usize) -> [u8; IP_ADDR_LEN] {
331 let mut a = [0u8; IP_ADDR_LEN];
332 a.copy_from_slice(&body[pos..pos + IP_ADDR_LEN]);
333 a
334}
335
336impl<'a> Parse<'a> for CommsIpConfigReply {
337 type Error = Error;
338 fn parse(bytes: &'a [u8]) -> Result<Self> {
339 let body =
340 objects::parse_apdu_header(bytes, tag::COMMS_IP_CONFIG_REPLY, "comms_IP_config_reply")?;
341 if body.len() < IP_CONFIG_PREFIX {
342 return Err(Error::BufferTooShort {
343 need: IP_CONFIG_PREFIX,
344 have: body.len(),
345 what: "comms_IP_config_reply",
346 });
347 }
348 let connection_state = ConnectionState::from_u8(body[0] >> 6);
350 let mut physical_address = [0u8; MAC_LEN];
351 physical_address.copy_from_slice(&body[1..1 + MAC_LEN]);
352 let ip_config = if connection_state.to_u8() == CONNECTION_STATE_CONNECTED {
353 if body.len() < IP_CONFIG_PREFIX + IP_CONFIG_FIXED {
354 return Err(Error::BufferTooShort {
355 need: IP_CONFIG_PREFIX + IP_CONFIG_FIXED,
356 have: body.len(),
357 what: "comms_IP_config_reply ip_config",
358 });
359 }
360 let mut p = IP_CONFIG_PREFIX;
361 let ip_address = read_addr(body, p);
362 p += IP_ADDR_LEN;
363 let network_mask = read_addr(body, p);
364 p += IP_ADDR_LEN;
365 let default_gateway = read_addr(body, p);
366 p += IP_ADDR_LEN;
367 let dhcp_server_address = read_addr(body, p);
368 p += IP_ADDR_LEN;
369 let n = body[p] as usize;
370 p += 1;
371 if body.len() < p + n * IP_ADDR_LEN {
372 return Err(Error::BufferTooShort {
373 need: p + n * IP_ADDR_LEN,
374 have: body.len(),
375 what: "comms_IP_config_reply dns_servers",
376 });
377 }
378 let mut dns_server_addresses = Vec::with_capacity(n);
379 for _ in 0..n {
380 dns_server_addresses.push(read_addr(body, p));
381 p += IP_ADDR_LEN;
382 }
383 Some(IpConfig {
384 ip_address,
385 network_mask,
386 default_gateway,
387 dhcp_server_address,
388 dns_server_addresses,
389 })
390 } else {
391 None
392 };
393 Ok(Self {
394 connection_state,
395 physical_address,
396 ip_config,
397 })
398 }
399}
400impl Serialize for CommsIpConfigReply {
401 type Error = Error;
402 fn serialized_len(&self) -> usize {
403 objects::apdu_len(self.body_len())
404 }
405 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
406 let body_len = self.body_len();
407 let mut pos = objects::write_apdu_header(tag::COMMS_IP_CONFIG_REPLY, body_len, buf)?;
408 buf[pos] = self.connection_state.to_u8() << 6;
410 buf[pos + 1..pos + 1 + MAC_LEN].copy_from_slice(&self.physical_address);
411 pos += IP_CONFIG_PREFIX;
412 if let Some(c) = &self.ip_config {
413 buf[pos..pos + IP_ADDR_LEN].copy_from_slice(&c.ip_address);
414 pos += IP_ADDR_LEN;
415 buf[pos..pos + IP_ADDR_LEN].copy_from_slice(&c.network_mask);
416 pos += IP_ADDR_LEN;
417 buf[pos..pos + IP_ADDR_LEN].copy_from_slice(&c.default_gateway);
418 pos += IP_ADDR_LEN;
419 buf[pos..pos + IP_ADDR_LEN].copy_from_slice(&c.dhcp_server_address);
420 pos += IP_ADDR_LEN;
421 buf[pos] = c.dns_server_addresses.len() as u8;
422 pos += 1;
423 for a in &c.dns_server_addresses {
424 buf[pos..pos + IP_ADDR_LEN].copy_from_slice(a);
425 pos += IP_ADDR_LEN;
426 }
427 }
428 Ok(pos)
429 }
430}
431
432pub const HYBRID_DESCRIPTOR_TAG: u8 = 0x05;
438pub const MULTICAST_DESCRIPTOR_TAG: u8 = 0x06;
440
441#[derive(Debug, Clone, Copy, PartialEq, Eq)]
444#[cfg_attr(feature = "serde", derive(serde::Serialize))]
445#[non_exhaustive]
446pub enum IpConnectionType {
447 IpDescriptor,
449 HostnameDescriptor,
451 MulticastDescriptor,
453 Reserved(u8),
455}
456impl IpConnectionType {
457 #[must_use]
459 pub fn from_u8(v: u8) -> Self {
460 match v {
461 0x03 => Self::IpDescriptor,
462 0x04 => Self::HostnameDescriptor,
463 0x06 => Self::MulticastDescriptor,
464 other => Self::Reserved(other),
465 }
466 }
467 #[must_use]
469 pub const fn to_u8(self) -> u8 {
470 match self {
471 Self::IpDescriptor => 0x03,
472 Self::HostnameDescriptor => 0x04,
473 Self::MulticastDescriptor => 0x06,
474 Self::Reserved(v) => v,
475 }
476 }
477 #[must_use]
479 pub fn name(&self) -> &'static str {
480 match self {
481 Self::IpDescriptor => "ip_descriptor",
482 Self::HostnameDescriptor => "hostname_descriptor",
483 Self::MulticastDescriptor => "multicast_descriptor",
484 Self::Reserved(_) => "reserved",
485 }
486 }
487}
488broadcast_common::impl_spec_display!(IpConnectionType, Reserved);
489
490#[derive(Debug, Clone, PartialEq, Eq)]
496#[cfg_attr(feature = "serde", derive(serde::Serialize))]
497pub struct HybridDescriptor<'a> {
498 pub lts_id: u8,
500 pub ip_connection_type: IpConnectionType,
502 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
504 pub inner: &'a [u8],
505}
506
507const HYBRID_FIXED: usize = 1 + 1;
509
510impl<'a> HybridDescriptor<'a> {
511 pub fn parse(bytes: &'a [u8]) -> Result<Self> {
514 let data = parse_descriptor_header(bytes, HYBRID_DESCRIPTOR_TAG, "hybrid_descriptor")?;
515 if data.len() < HYBRID_FIXED {
516 return Err(Error::BufferTooShort {
517 need: HYBRID_FIXED,
518 have: data.len(),
519 what: "hybrid_descriptor",
520 });
521 }
522 Ok(Self {
523 lts_id: data[0],
524 ip_connection_type: IpConnectionType::from_u8(data[1]),
525 inner: &data[HYBRID_FIXED..],
526 })
527 }
528 fn data_len(&self) -> usize {
529 HYBRID_FIXED + self.inner.len()
530 }
531}
532
533impl Serialize for HybridDescriptor<'_> {
534 type Error = Error;
535 fn serialized_len(&self) -> usize {
536 descriptor_len(self.data_len())
537 }
538 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
539 let pos = write_descriptor_header(HYBRID_DESCRIPTOR_TAG, self.data_len(), buf)?;
540 buf[pos] = self.lts_id;
541 buf[pos + 1] = self.ip_connection_type.to_u8();
542 buf[pos + HYBRID_FIXED..pos + self.data_len()].copy_from_slice(self.inner);
543 Ok(pos + self.data_len())
544 }
545}
546
547#[derive(Debug, Clone, PartialEq, Eq)]
553#[cfg_attr(feature = "serde", derive(serde::Serialize))]
554pub struct MulticastDescriptor {
555 pub ip_protocol_version: IpProtocolVersion,
557 pub ip_address: [u8; IP_ADDR_LEN],
559 pub multicast_port: u16,
561 pub include_sources: bool,
565 pub source_addresses: Vec<[u8; IP_ADDR_LEN]>,
567}
568
569const MULTICAST_FIXED: usize = 1 + IP_ADDR_LEN + 2 + 1 + 1;
572const INCLUDE_SOURCES_BIT: u8 = 0x01;
573
574impl MulticastDescriptor {
575 pub fn parse(bytes: &[u8]) -> Result<Self> {
578 let data =
579 parse_descriptor_header(bytes, MULTICAST_DESCRIPTOR_TAG, "multicast_descriptor")?;
580 if data.len() < MULTICAST_FIXED {
581 return Err(Error::BufferTooShort {
582 need: MULTICAST_FIXED,
583 have: data.len(),
584 what: "multicast_descriptor",
585 });
586 }
587 let ip_protocol_version = IpProtocolVersion::from_u8(data[0]);
588 let ip_address = read_addr(data, 1);
589 let multicast_port = u16::from_be_bytes([data[1 + IP_ADDR_LEN], data[2 + IP_ADDR_LEN]]);
590 let flags_pos = 3 + IP_ADDR_LEN;
591 let include_sources = data[flags_pos] & INCLUDE_SOURCES_BIT != 0;
592 let n = data[flags_pos + 1] as usize;
593 let mut pos = MULTICAST_FIXED;
594 if data.len() < pos + n * IP_ADDR_LEN {
595 return Err(Error::BufferTooShort {
596 need: pos + n * IP_ADDR_LEN,
597 have: data.len(),
598 what: "multicast_descriptor sources",
599 });
600 }
601 let mut source_addresses = Vec::with_capacity(n);
602 for _ in 0..n {
603 source_addresses.push(read_addr(data, pos));
604 pos += IP_ADDR_LEN;
605 }
606 Ok(Self {
607 ip_protocol_version,
608 ip_address,
609 multicast_port,
610 include_sources,
611 source_addresses,
612 })
613 }
614 fn data_len(&self) -> usize {
615 MULTICAST_FIXED + self.source_addresses.len() * IP_ADDR_LEN
616 }
617}
618
619impl Serialize for MulticastDescriptor {
620 type Error = Error;
621 fn serialized_len(&self) -> usize {
622 descriptor_len(self.data_len())
623 }
624 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
625 let mut pos = write_descriptor_header(MULTICAST_DESCRIPTOR_TAG, self.data_len(), buf)?;
626 buf[pos] = self.ip_protocol_version.to_u8();
627 buf[pos + 1..pos + 1 + IP_ADDR_LEN].copy_from_slice(&self.ip_address);
628 let p = pos + 1 + IP_ADDR_LEN;
629 buf[p..p + 2].copy_from_slice(&self.multicast_port.to_be_bytes());
630 buf[p + 2] = u8::from(self.include_sources);
632 buf[p + 3] = self.source_addresses.len() as u8;
633 pos += MULTICAST_FIXED;
634 for a in &self.source_addresses {
635 buf[pos..pos + IP_ADDR_LEN].copy_from_slice(a);
636 pos += IP_ADDR_LEN;
637 }
638 Ok(pos)
639 }
640}
641
642const DESCRIPTOR_HEADER: usize = 2;
646
647fn parse_descriptor_header<'a>(
648 bytes: &'a [u8],
649 expected_tag: u8,
650 what: &'static str,
651) -> Result<&'a [u8]> {
652 if bytes.len() < DESCRIPTOR_HEADER {
653 return Err(Error::BufferTooShort {
654 need: DESCRIPTOR_HEADER,
655 have: bytes.len(),
656 what,
657 });
658 }
659 if bytes[0] != expected_tag {
660 return Err(Error::InvalidObject {
661 what,
662 reason: "unexpected descriptor_tag",
663 });
664 }
665 let len = bytes[1] as usize;
666 let end = DESCRIPTOR_HEADER + len;
667 if bytes.len() < end {
668 return Err(Error::LengthMismatch {
669 what,
670 declared: len,
671 actual: bytes.len().saturating_sub(DESCRIPTOR_HEADER),
672 });
673 }
674 Ok(&bytes[DESCRIPTOR_HEADER..end])
675}
676
677fn descriptor_len(data_len: usize) -> usize {
678 DESCRIPTOR_HEADER + data_len
679}
680
681fn write_descriptor_header(tag: u8, data_len: usize, buf: &mut [u8]) -> Result<usize> {
682 let total = descriptor_len(data_len);
683 if buf.len() < total {
684 return Err(Error::OutputBufferTooSmall {
685 need: total,
686 have: buf.len(),
687 });
688 }
689 if data_len > u8::MAX as usize {
690 return Err(Error::LengthTooLarge(data_len));
691 }
692 buf[0] = tag;
693 buf[1] = data_len as u8;
694 Ok(DESCRIPTOR_HEADER)
695}
696
697#[derive(Debug, Clone, Copy, PartialEq, Eq)]
707#[cfg_attr(feature = "serde", derive(serde::Serialize))]
708#[non_exhaustive]
709pub enum LscV4Apdu {
710 CommsInfoReq(CommsInfoReq),
712 CommsInfoReply(CommsInfoReply),
714 CommsIpConfigReq(CommsIpConfigReq),
716}
717
718#[derive(Debug, Clone, PartialEq, Eq)]
721#[cfg_attr(feature = "serde", derive(serde::Serialize))]
722#[non_exhaustive]
723pub enum LscV4ReplyApdu {
724 CommsIpConfigReply(CommsIpConfigReply),
726}
727
728impl LscV4Apdu {
729 pub fn parse(body: &[u8]) -> Result<Self> {
733 if body.len() < 3 {
734 return Err(Error::BufferTooShort {
735 need: 3,
736 have: body.len(),
737 what: "lsc_v4 apdu_tag",
738 });
739 }
740 let t = ApduTag::from_bytes(body[0], body[1], body[2]);
741 match t {
742 tag::COMMS_INFO_REQ => Ok(Self::CommsInfoReq(CommsInfoReq::parse(body)?)),
743 tag::COMMS_INFO_REPLY => Ok(Self::CommsInfoReply(CommsInfoReply::parse(body)?)),
744 tag::COMMS_IP_CONFIG_REQ => Ok(Self::CommsIpConfigReq(CommsIpConfigReq::parse(body)?)),
745 _ => Err(Error::UnexpectedApduTag {
746 got: t.as_u24(),
747 expected: tag::COMMS_INFO_REQ.as_u24(),
748 what: "lsc_v4",
749 }),
750 }
751 }
752}
753
754pub fn parse_ip_config_reply(body: &[u8]) -> Result<CommsIpConfigReply> {
756 CommsIpConfigReply::parse(body)
757}
758
759impl Serialize for LscV4Apdu {
760 type Error = Error;
761 fn serialized_len(&self) -> usize {
762 match self {
763 Self::CommsInfoReq(o) => o.serialized_len(),
764 Self::CommsInfoReply(o) => o.serialized_len(),
765 Self::CommsIpConfigReq(o) => o.serialized_len(),
766 }
767 }
768 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
769 match self {
770 Self::CommsInfoReq(o) => o.serialize_into(buf),
771 Self::CommsInfoReply(o) => o.serialize_into(buf),
772 Self::CommsIpConfigReq(o) => o.serialize_into(buf),
773 }
774 }
775}
776
777impl Serialize for LscV4ReplyApdu {
778 type Error = Error;
779 fn serialized_len(&self) -> usize {
780 match self {
781 Self::CommsIpConfigReply(o) => o.serialized_len(),
782 }
783 }
784 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
785 match self {
786 Self::CommsIpConfigReply(o) => o.serialize_into(buf),
787 }
788 }
789}
790
791#[cfg(test)]
792mod tests {
793 use super::*;
794
795 const IP_A: [u8; IP_ADDR_LEN] = [
796 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0xA8, 0x01,
797 0x0A,
798 ];
799 const IP_B: [u8; IP_ADDR_LEN] = [
800 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x08, 0x08, 0x08,
801 0x08,
802 ];
803
804 #[test]
805 fn info_req_round_trips() {
806 let bytes = CommsInfoReq.to_bytes();
807 assert_eq!(bytes, [0x9F, 0x8C, 0x07, 0x00]);
808 assert_eq!(CommsInfoReq::parse(&bytes).unwrap(), CommsInfoReq);
809 }
810
811 #[test]
812 fn info_reply_round_trips_and_bites() {
813 let r = CommsInfoReply {
814 lts_id: 0x07,
815 status: true,
816 source_ip_address: IP_A,
817 source_port: 0x1234,
818 input_delivery_pid: 0x0100,
819 };
820 let bytes = r.to_bytes();
821 assert_eq!(bytes[0..4], [0x9F, 0x8C, 0x08, 0x16]);
823 assert_eq!(bytes[4], 0x07);
824 assert_eq!(bytes[5], 0x01); assert_eq!(&bytes[6..22], &IP_A);
826 assert_eq!(&bytes[22..24], &[0x12, 0x34]);
827 assert_eq!(&bytes[24..26], &[0x01, 0x00]);
828 assert_eq!(CommsInfoReply::parse(&bytes).unwrap(), r);
829 let mut other = r;
830 other.status = false;
831 assert_eq!(other.to_bytes()[5], 0x00);
832 assert_ne!(bytes, other.to_bytes());
833 }
834
835 #[test]
836 fn info_reply_pid_is_13_bit_masked() {
837 let bytes = [
839 0x9F, 0x8C, 0x08, 0x16, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
840 0x00, 0x00, 0xFF, 0xFE,
841 ];
842 let r = CommsInfoReply::parse(&bytes).unwrap();
843 assert_eq!(r.input_delivery_pid, 0x1FFE);
844 }
845
846 #[test]
847 fn ip_config_req_round_trips() {
848 let bytes = CommsIpConfigReq.to_bytes();
849 assert_eq!(bytes, [0x9F, 0x8C, 0x09, 0x00]);
850 assert_eq!(CommsIpConfigReq::parse(&bytes).unwrap(), CommsIpConfigReq);
851 }
852
853 #[test]
854 fn ip_config_reply_disconnected_round_trips() {
855 let r = CommsIpConfigReply {
856 connection_state: ConnectionState::Disconnected,
857 physical_address: [0x00, 0x11, 0x22, 0x33, 0x44, 0x55],
858 ip_config: None,
859 };
860 let bytes = r.to_bytes();
861 assert_eq!(
863 bytes,
864 [
865 0x9F, 0x8C, 0x0A, 0x07, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55
866 ]
867 );
868 assert_eq!(CommsIpConfigReply::parse(&bytes).unwrap(), r);
869 }
870
871 #[test]
872 fn ip_config_reply_connected_two_dns_round_trips_and_bites() {
873 let r = CommsIpConfigReply {
874 connection_state: ConnectionState::Connected,
875 physical_address: [0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F],
876 ip_config: Some(IpConfig {
877 ip_address: IP_A,
878 network_mask: IP_B,
879 default_gateway: IP_A,
880 dhcp_server_address: IP_B,
881 dns_server_addresses: alloc::vec![IP_A, IP_B],
882 }),
883 };
884 let bytes = r.to_bytes();
885 assert_eq!(bytes[0..4], [0x9F, 0x8C, 0x0A, (7 + 65 + 32) as u8]);
887 assert_eq!(bytes[4], 0x40);
888 assert_eq!(&bytes[5..11], &[0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F]);
889 assert_eq!(bytes[4 + 7 + 4 * IP_ADDR_LEN], 0x02);
891 assert_eq!(CommsIpConfigReply::parse(&bytes).unwrap(), r);
892 let mut other = r.clone();
893 if let Some(c) = &mut other.ip_config {
894 c.dns_server_addresses.pop();
895 }
896 assert_ne!(bytes, other.to_bytes());
897 }
898
899 #[test]
900 fn hybrid_descriptor_round_trips_and_bites() {
901 let h = HybridDescriptor {
902 lts_id: 0x05,
903 ip_connection_type: IpConnectionType::IpDescriptor,
904 inner: &[0xDE, 0xAD],
905 };
906 let bytes = h.to_bytes();
907 assert_eq!(bytes, [0x05, 0x04, 0x05, 0x03, 0xDE, 0xAD]);
909 assert_eq!(HybridDescriptor::parse(&bytes).unwrap(), h);
910 let mut other = h;
911 other.lts_id = 0x06;
912 assert_ne!(bytes, other.to_bytes());
913 }
914
915 #[test]
916 fn multicast_descriptor_two_sources_round_trips_and_bites() {
917 let m = MulticastDescriptor {
918 ip_protocol_version: IpProtocolVersion::Ipv4,
919 ip_address: IP_A,
920 multicast_port: 0x1389,
921 include_sources: true,
922 source_addresses: alloc::vec![IP_A, IP_B],
923 };
924 let bytes = m.to_bytes();
925 assert_eq!(bytes[0], MULTICAST_DESCRIPTOR_TAG);
927 assert_eq!(bytes[1], 53);
928 assert_eq!(bytes[2], 0x01); assert_eq!(&bytes[3..19], &IP_A);
930 assert_eq!(&bytes[19..21], &[0x13, 0x89]);
931 assert_eq!(bytes[21], 0x01); assert_eq!(bytes[22], 0x02); assert_eq!(MulticastDescriptor::parse(&bytes).unwrap(), m);
934 let mut other = m.clone();
935 other.include_sources = false;
936 assert_eq!(other.to_bytes()[21], 0x00);
937 assert_ne!(bytes, other.to_bytes());
938 }
939
940 #[test]
941 fn multicast_descriptor_any_source() {
942 let m = MulticastDescriptor {
943 ip_protocol_version: IpProtocolVersion::Ipv6,
944 ip_address: IP_B,
945 multicast_port: 5004,
946 include_sources: false,
947 source_addresses: Vec::new(),
948 };
949 let bytes = m.to_bytes();
950 assert_eq!(bytes[1], 21); assert_eq!(MulticastDescriptor::parse(&bytes).unwrap(), m);
952 }
953
954 #[test]
955 fn dispatch_routes_fixed_tags() {
956 assert!(matches!(
957 LscV4Apdu::parse(&CommsInfoReq.to_bytes()).unwrap(),
958 LscV4Apdu::CommsInfoReq(_)
959 ));
960 let reply = CommsInfoReply {
961 lts_id: 0,
962 status: false,
963 source_ip_address: [0; IP_ADDR_LEN],
964 source_port: 0,
965 input_delivery_pid: 0,
966 };
967 assert!(matches!(
968 LscV4Apdu::parse(&reply.to_bytes()).unwrap(),
969 LscV4Apdu::CommsInfoReply(_)
970 ));
971 assert!(matches!(
972 LscV4Apdu::parse(&CommsIpConfigReq.to_bytes()).unwrap(),
973 LscV4Apdu::CommsIpConfigReq(_)
974 ));
975 let cfg = CommsIpConfigReply {
977 connection_state: ConnectionState::Disconnected,
978 physical_address: [0; MAC_LEN],
979 ip_config: None,
980 };
981 let cb = cfg.to_bytes();
982 assert_eq!(parse_ip_config_reply(&cb).unwrap(), cfg);
983 assert!(matches!(
985 LscV4Apdu::parse(&cb),
986 Err(Error::UnexpectedApduTag { .. })
987 ));
988 }
989}