#[cfg(feature = "std")]
use std::error::Error;
#[cfg(feature = "std")]
use std::{fmt, time::Duration};
#[cfg(not(feature = "std"))]
use core::fmt;
#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
#[cfg(not(feature = "std"))]
use core::time::Duration;
use crate::encoding::{decode_enumerated, encode_enumerated};
use crate::object::Segmentation;
use crate::service::{AbortReason, ConfirmedServiceChoice, RejectReason, UnconfirmedServiceChoice};
#[cfg(feature = "std")]
pub type Result<T> = std::result::Result<T, ApplicationError>;
#[cfg(not(feature = "std"))]
pub type Result<T> = core::result::Result<T, ApplicationError>;
#[derive(Debug)]
pub enum ApplicationError {
InvalidApdu(String),
UnsupportedApduType,
SegmentationError(String),
TransactionError(String),
ServiceError(String),
Timeout,
MaxApduLengthExceeded,
}
impl fmt::Display for ApplicationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ApplicationError::InvalidApdu(msg) => write!(f, "Invalid APDU: {}", msg),
ApplicationError::UnsupportedApduType => write!(f, "Unsupported APDU type"),
ApplicationError::SegmentationError(msg) => write!(f, "Segmentation error: {}", msg),
ApplicationError::TransactionError(msg) => write!(f, "Transaction error: {}", msg),
ApplicationError::ServiceError(msg) => write!(f, "Service error: {}", msg),
ApplicationError::Timeout => write!(f, "Application timeout"),
ApplicationError::MaxApduLengthExceeded => write!(f, "Maximum APDU length exceeded"),
}
}
}
#[cfg(feature = "std")]
impl Error for ApplicationError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ApduType {
ConfirmedRequest = 0,
UnconfirmedRequest = 1,
SimpleAck = 2,
ComplexAck = 3,
SegmentAck = 4,
Error = 5,
Reject = 6,
Abort = 7,
}
#[derive(Debug, Clone)]
pub enum Apdu {
ConfirmedRequest {
segmented: bool,
more_follows: bool,
segmented_response_accepted: bool,
max_segments: MaxSegments,
max_response_size: MaxApduSize,
invoke_id: u8,
sequence_number: Option<u8>,
proposed_window_size: Option<u8>,
service_choice: ConfirmedServiceChoice,
service_data: Vec<u8>,
},
UnconfirmedRequest {
service_choice: UnconfirmedServiceChoice,
service_data: Vec<u8>,
},
SimpleAck { invoke_id: u8, service_choice: u8 },
ComplexAck {
segmented: bool,
more_follows: bool,
invoke_id: u8,
sequence_number: Option<u8>,
proposed_window_size: Option<u8>,
service_choice: ConfirmedServiceChoice,
service_data: Vec<u8>,
},
SegmentAck {
negative: bool,
server: bool,
invoke_id: u8,
sequence_number: u8,
window_size: u8,
},
Error {
invoke_id: u8,
service_choice: ConfirmedServiceChoice,
error_class: u8,
error_code: u8,
},
Reject {
invoke_id: u8,
reject_reason: RejectReason,
},
Abort {
server: bool,
invoke_id: u8,
abort_reason: u8,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MaxSegments {
Unspecified = 0,
Two = 1,
Four = 2,
Eight = 3,
Sixteen = 4,
ThirtyTwo = 5,
SixtyFour = 6,
GreaterThan64 = 7,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MaxApduSize {
Up50 = 0,
Up128 = 1,
Up206 = 2,
Up480 = 3,
Up1024 = 4,
Up1476 = 5,
}
impl MaxApduSize {
pub fn size(&self) -> usize {
match self {
MaxApduSize::Up50 => 50,
MaxApduSize::Up128 => 128,
MaxApduSize::Up206 => 206,
MaxApduSize::Up480 => 480,
MaxApduSize::Up1024 => 1024,
MaxApduSize::Up1476 => 1476,
}
}
}
#[derive(Debug, Clone)]
pub struct Transaction {
pub invoke_id: u8,
pub service: u8,
pub state: TransactionState,
pub timeout: Duration,
pub retries: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TransactionState {
AwaitConfirmation,
AwaitSegment,
SegmentedRequest,
SegmentedResponse,
Complete,
}
impl Apdu {
pub fn encode(&self) -> Vec<u8> {
let mut buffer = Vec::new();
match self {
Apdu::ConfirmedRequest {
segmented,
more_follows,
segmented_response_accepted,
max_segments,
max_response_size,
invoke_id,
sequence_number,
proposed_window_size,
service_choice,
service_data,
} => {
let mut pdu_type = (ApduType::ConfirmedRequest as u8) << 4;
if *segmented {
pdu_type |= 0x08;
}
if *more_follows {
pdu_type |= 0x04;
}
if *segmented_response_accepted {
pdu_type |= 0x02;
}
buffer.push(pdu_type);
let max_info = ((*max_segments as u8) << 4) | (*max_response_size as u8);
buffer.push(max_info);
buffer.push(*invoke_id);
if *segmented {
if let Some(seq_num) = sequence_number {
buffer.push(*seq_num);
}
if let Some(window_size) = proposed_window_size {
buffer.push(*window_size);
}
}
buffer.push(*service_choice as u8);
buffer.extend_from_slice(service_data);
}
Apdu::UnconfirmedRequest {
service_choice,
service_data,
} => {
buffer.push((ApduType::UnconfirmedRequest as u8) << 4);
buffer.push(*service_choice as u8);
buffer.extend_from_slice(service_data);
}
Apdu::SimpleAck {
invoke_id,
service_choice,
} => {
buffer.push((ApduType::SimpleAck as u8) << 4);
buffer.push(*invoke_id);
buffer.push(*service_choice);
}
Apdu::ComplexAck {
segmented,
more_follows,
invoke_id,
sequence_number,
proposed_window_size,
service_choice,
service_data,
} => {
let mut pdu_type = (ApduType::ComplexAck as u8) << 4;
if *segmented {
pdu_type |= 0x08;
}
if *more_follows {
pdu_type |= 0x04;
}
buffer.push(pdu_type);
buffer.push(*invoke_id);
if *segmented {
if let Some(seq_num) = sequence_number {
buffer.push(*seq_num);
}
if let Some(window_size) = proposed_window_size {
buffer.push(*window_size);
}
}
buffer.push(*service_choice as u8);
buffer.extend_from_slice(service_data);
}
Apdu::SegmentAck {
negative,
server,
invoke_id,
sequence_number,
window_size,
} => {
let mut pdu_type = (ApduType::SegmentAck as u8) << 4;
if *negative {
pdu_type |= 0x02;
}
if *server {
pdu_type |= 0x01;
}
buffer.push(pdu_type);
buffer.push(*invoke_id);
buffer.push(*sequence_number);
buffer.push(*window_size);
}
Apdu::Error {
invoke_id,
service_choice,
error_class,
error_code,
} => {
buffer.push((ApduType::Error as u8) << 4);
buffer.push(*invoke_id);
buffer.push(*service_choice as u8);
encode_enumerated(&mut buffer, *error_class as u32);
encode_enumerated(&mut buffer, *error_code as u32);
}
Apdu::Reject {
invoke_id,
reject_reason,
} => {
buffer.push((ApduType::Reject as u8) << 4);
buffer.push(*invoke_id);
buffer.push(u8::from(*reject_reason));
}
Apdu::Abort {
server,
invoke_id,
abort_reason,
} => {
let mut pdu_type = (ApduType::Abort as u8) << 4;
if *server {
pdu_type |= 0x01;
}
buffer.push(pdu_type);
buffer.push(*invoke_id);
buffer.push(*abort_reason);
}
}
buffer
}
pub fn decode(data: &[u8]) -> Result<Self> {
if data.is_empty() {
return Err(ApplicationError::InvalidApdu("Empty APDU".to_string()));
}
let pdu_type_byte = data[0];
let pdu_type_raw = (pdu_type_byte >> 4) & 0x0F;
let pdu_type = match pdu_type_raw {
0 => ApduType::ConfirmedRequest,
1 => ApduType::UnconfirmedRequest,
2 => ApduType::SimpleAck,
3 => ApduType::ComplexAck,
4 => ApduType::SegmentAck,
5 => ApduType::Error,
6 => ApduType::Reject,
7 => ApduType::Abort,
_ => return Err(ApplicationError::UnsupportedApduType),
};
match pdu_type {
ApduType::ConfirmedRequest => {
if data.len() < 4 {
return Err(ApplicationError::InvalidApdu(
"Confirmed request too short".to_string(),
));
}
let segmented = (pdu_type_byte & 0x08) != 0;
let more_follows = (pdu_type_byte & 0x04) != 0;
let segmented_response_accepted = (pdu_type_byte & 0x02) != 0;
let max_info = data[1];
let max_segments = match (max_info >> 4) & 0x07 {
0 => MaxSegments::Unspecified,
1 => MaxSegments::Two,
2 => MaxSegments::Four,
3 => MaxSegments::Eight,
4 => MaxSegments::Sixteen,
5 => MaxSegments::ThirtyTwo,
6 => MaxSegments::SixtyFour,
7 => MaxSegments::GreaterThan64,
_ => MaxSegments::Unspecified,
};
let max_response_size = match max_info & 0x0F {
0 => MaxApduSize::Up50,
1 => MaxApduSize::Up128,
2 => MaxApduSize::Up206,
3 => MaxApduSize::Up480,
4 => MaxApduSize::Up1024,
5 => MaxApduSize::Up1476,
_ => MaxApduSize::Up50,
};
let invoke_id = data[2];
let mut pos = 3;
let (sequence_number, proposed_window_size) = if segmented {
let seq_num = if pos < data.len() {
Some(data[pos])
} else {
None
};
pos += 1;
let win_size = if pos < data.len() {
Some(data[pos])
} else {
None
};
pos += 1;
(seq_num, win_size)
} else {
(None, None)
};
if pos >= data.len() {
return Err(ApplicationError::InvalidApdu(
"Missing service choice".to_string(),
));
}
let service_choice = data[pos].try_into().map_err(|_| {
ApplicationError::InvalidApdu("Unknown confirmed service choice".to_string())
})?;
pos += 1;
let service_data = if pos < data.len() {
data[pos..].to_vec()
} else {
Vec::new()
};
Ok(Apdu::ConfirmedRequest {
segmented,
more_follows,
segmented_response_accepted,
max_segments,
max_response_size,
invoke_id,
sequence_number,
proposed_window_size,
service_choice,
service_data,
})
}
ApduType::UnconfirmedRequest => {
if data.len() < 2 {
return Err(ApplicationError::InvalidApdu(
"Unconfirmed request too short".to_string(),
));
}
let service_choice = data[1];
let service_data = if data.len() > 2 {
data[2..].to_vec()
} else {
Vec::new()
};
Ok(Apdu::UnconfirmedRequest {
service_choice: service_choice.try_into().map_err(|_| {
ApplicationError::InvalidApdu(
"Unknown unconfirmed service choice".to_string(),
)
})?,
service_data,
})
}
ApduType::SimpleAck => {
if data.len() < 3 {
return Err(ApplicationError::InvalidApdu(
"SimpleAck too short".to_string(),
));
}
let invoke_id = data[1];
let service_choice = data[2];
Ok(Apdu::SimpleAck {
invoke_id,
service_choice,
})
}
ApduType::ComplexAck => {
if data.len() < 3 {
return Err(ApplicationError::InvalidApdu(
"ComplexAck too short".to_string(),
));
}
let segmented = (pdu_type_byte & 0x08) != 0;
let more_follows = (pdu_type_byte & 0x04) != 0;
let invoke_id = data[1];
let mut pos = 2;
let (sequence_number, proposed_window_size) = if segmented {
let seq_num = if pos < data.len() {
Some(data[pos])
} else {
None
};
pos += 1;
let win_size = if pos < data.len() {
Some(data[pos])
} else {
None
};
pos += 1;
(seq_num, win_size)
} else {
(None, None)
};
if pos >= data.len() {
return Err(ApplicationError::InvalidApdu(
"Missing service choice".to_string(),
));
}
let service_choice = data[pos].try_into().map_err(|_| {
ApplicationError::InvalidApdu("Unknown confirmed service choice".to_string())
})?;
pos += 1;
let service_data = if pos < data.len() {
data[pos..].to_vec()
} else {
Vec::new()
};
Ok(Apdu::ComplexAck {
segmented,
more_follows,
invoke_id,
sequence_number,
proposed_window_size,
service_choice,
service_data,
})
}
ApduType::SegmentAck => {
if data.len() < 4 {
return Err(ApplicationError::InvalidApdu(
"SegmentAck too short".to_string(),
));
}
let negative = (pdu_type_byte & 0x02) != 0;
let server = (pdu_type_byte & 0x01) != 0;
let invoke_id = data[1];
let sequence_number = data[2];
let window_size = data[3];
Ok(Apdu::SegmentAck {
negative,
server,
invoke_id,
sequence_number,
window_size,
})
}
ApduType::Error => {
if data.len() < 5 {
return Err(ApplicationError::InvalidApdu(
"Error PDU too short".to_string(),
));
}
let invoke_id = data[1];
let mut pos = 2;
let service_choice = data[pos].try_into().map_err(|_| {
ApplicationError::InvalidApdu("Unknown confirmed service choice".to_string())
})?;
pos += 1;
let (error_class, offset) = decode_enumerated(&data[pos..]).map_err(|_| {
ApplicationError::InvalidApdu("Invalid error class".to_string())
})?;
pos += offset;
let (error_code, _) = decode_enumerated(&data[pos..])
.map_err(|_| ApplicationError::InvalidApdu("Invalid error code".to_string()))?;
Ok(Apdu::Error {
invoke_id,
service_choice,
error_class: error_class as u8,
error_code: error_code as u8,
})
}
ApduType::Reject => {
if data.len() < 3 {
return Err(ApplicationError::InvalidApdu(
"Reject PDU too short".to_string(),
));
}
let invoke_id = data[1];
let reject_reason = data[2];
Ok(Apdu::Reject {
invoke_id,
reject_reason: reject_reason.into(),
})
}
ApduType::Abort => {
if data.len() < 3 {
return Err(ApplicationError::InvalidApdu(
"Abort PDU too short".to_string(),
));
}
let server = (pdu_type_byte & 0x01) != 0;
let invoke_id = data[1];
let abort_reason = data[2];
Ok(Apdu::Abort {
server,
invoke_id,
abort_reason,
})
}
}
}
}
#[derive(Debug)]
pub struct InvokeIdManager {
next_id: u8,
active_ids: Vec<u8>,
}
impl InvokeIdManager {
pub fn new() -> Self {
Self {
next_id: 0,
active_ids: Vec::new(),
}
}
pub fn next_id(&mut self) -> Option<u8> {
let start_id = self.next_id;
loop {
if !self.active_ids.contains(&self.next_id) {
let id = self.next_id;
self.active_ids.push(id);
self.next_id = self.next_id.wrapping_add(1);
return Some(id);
}
self.next_id = self.next_id.wrapping_add(1);
if self.next_id == start_id {
return None;
}
}
}
pub fn release_id(&mut self, id: u8) {
self.active_ids.retain(|&x| x != id);
}
pub fn is_active(&self, id: u8) -> bool {
self.active_ids.contains(&id)
}
}
impl Default for InvokeIdManager {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SegmentationInfo {
pub more_follows: bool,
pub segmented_response_accepted: bool,
pub max_segments_accepted: u8,
pub max_apdu_length_accepted: u16,
pub sequence_number: u8,
pub proposed_window_size: u8,
}
impl SegmentationInfo {
pub fn new(
more_follows: bool,
segmented_response_accepted: bool,
max_segments_accepted: u8,
max_apdu_length_accepted: u16,
sequence_number: u8,
proposed_window_size: u8,
) -> Self {
Self {
more_follows,
segmented_response_accepted,
max_segments_accepted,
max_apdu_length_accepted,
sequence_number,
proposed_window_size,
}
}
pub fn is_first_segment(&self) -> bool {
self.sequence_number == 0
}
pub fn is_last_segment(&self) -> bool {
!self.more_follows
}
pub fn max_segment_size(&self) -> usize {
(self.max_apdu_length_accepted as usize).saturating_sub(6)
}
}
#[derive(Debug)]
pub struct SegmentReassemblyBuffer {
pub invoke_id: u8,
pub total_segments: Option<u8>,
pub segments: Vec<(u8, Vec<u8>)>,
pub max_apdu_length: u16,
#[cfg(feature = "std")]
pub last_activity: std::time::Instant,
}
impl SegmentReassemblyBuffer {
pub fn new(invoke_id: u8, max_apdu_length: u16) -> Self {
Self {
invoke_id,
total_segments: None,
segments: Vec::new(),
max_apdu_length,
#[cfg(feature = "std")]
last_activity: std::time::Instant::now(),
}
}
pub fn add_segment(&mut self, sequence_number: u8, data: Vec<u8>, is_last: bool) -> Result<()> {
#[cfg(feature = "std")]
{
self.last_activity = std::time::Instant::now();
}
if is_last {
self.total_segments = Some(sequence_number + 1);
}
if self.segments.iter().any(|(seq, _)| *seq == sequence_number) {
return Ok(()); }
self.segments.push((sequence_number, data));
self.segments.sort_by_key(|(seq, _)| *seq);
Ok(())
}
pub fn is_complete(&self) -> bool {
if let Some(total) = self.total_segments {
self.segments.len() == total as usize
&& self
.segments
.iter()
.enumerate()
.all(|(i, (seq, _))| *seq == i as u8)
} else {
false
}
}
pub fn reassemble(&self) -> Result<Vec<u8>> {
if !self.is_complete() {
return Err(ApplicationError::SegmentationError(
"Incomplete segments".to_string(),
));
}
let mut result = Vec::new();
for (_, data) in &self.segments {
result.extend_from_slice(data);
}
if result.len() > self.max_apdu_length as usize {
return Err(ApplicationError::MaxApduLengthExceeded);
}
Ok(result)
}
pub fn missing_segments(&self) -> Vec<u8> {
if let Some(total) = self.total_segments {
let mut missing = Vec::new();
for i in 0..total {
if !self.segments.iter().any(|(seq, _)| *seq == i) {
missing.push(i);
}
}
missing
} else {
Vec::new()
}
}
#[cfg(feature = "std")]
pub fn is_timed_out(&self, timeout_duration: std::time::Duration) -> bool {
self.last_activity.elapsed() > timeout_duration
}
}
#[derive(Debug)]
pub struct SegmentationManager {
reassembly_buffers: Vec<SegmentReassemblyBuffer>,
max_concurrent_reassemblies: usize,
#[cfg(feature = "std")]
segment_timeout: std::time::Duration,
}
impl SegmentationManager {
pub fn new() -> Self {
Self {
reassembly_buffers: Vec::new(),
max_concurrent_reassemblies: 16,
#[cfg(feature = "std")]
segment_timeout: std::time::Duration::from_secs(60),
}
}
pub fn segment_message(
&self,
data: &[u8],
max_segment_size: usize,
max_segments: u8,
) -> Result<Vec<Vec<u8>>> {
if data.is_empty() {
return Ok(vec![Vec::new()]);
}
let segment_count = data.len().div_ceil(max_segment_size);
if segment_count > max_segments as usize {
return Err(ApplicationError::SegmentationError(
"Message too large for segmentation".to_string(),
));
}
let mut segments = Vec::new();
let mut offset = 0;
for _ in 0..segment_count {
let end = (offset + max_segment_size).min(data.len());
segments.push(data[offset..end].to_vec());
offset = end;
}
Ok(segments)
}
pub fn process_segment(
&mut self,
invoke_id: u8,
sequence_number: u8,
data: Vec<u8>,
more_follows: bool,
max_apdu_length: u16,
) -> Result<Option<Vec<u8>>> {
let buffer_index = self
.reassembly_buffers
.iter()
.position(|buffer| buffer.invoke_id == invoke_id);
let buffer = if let Some(index) = buffer_index {
&mut self.reassembly_buffers[index]
} else {
if self.reassembly_buffers.len() >= self.max_concurrent_reassemblies {
self.cleanup_oldest_buffer();
}
self.reassembly_buffers
.push(SegmentReassemblyBuffer::new(invoke_id, max_apdu_length));
self.reassembly_buffers.last_mut().unwrap()
};
buffer.add_segment(sequence_number, data, !more_follows)?;
if buffer.is_complete() {
let result = buffer.reassemble()?;
self.reassembly_buffers.retain(|b| b.invoke_id != invoke_id);
Ok(Some(result))
} else {
Ok(None)
}
}
pub fn get_missing_segments(&self, invoke_id: u8) -> Vec<u8> {
self.reassembly_buffers
.iter()
.find(|buffer| buffer.invoke_id == invoke_id)
.map(|buffer| buffer.missing_segments())
.unwrap_or_default()
}
#[cfg(feature = "std")]
pub fn cleanup_timed_out_buffers(&mut self) {
self.reassembly_buffers
.retain(|buffer| !buffer.is_timed_out(self.segment_timeout));
}
fn cleanup_oldest_buffer(&mut self) {
if !self.reassembly_buffers.is_empty() {
#[cfg(feature = "std")]
{
let oldest_index = self
.reassembly_buffers
.iter()
.enumerate()
.min_by_key(|(_, buffer)| buffer.last_activity)
.map(|(index, _)| index)
.unwrap_or(0);
self.reassembly_buffers.remove(oldest_index);
}
#[cfg(not(feature = "std"))]
{
self.reassembly_buffers.remove(0);
}
}
}
#[cfg(feature = "std")]
pub fn set_segment_timeout(&mut self, timeout: std::time::Duration) {
self.segment_timeout = timeout;
}
pub fn active_reassemblies(&self) -> usize {
self.reassembly_buffers.len()
}
}
impl Default for SegmentationManager {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct ApplicationLayerHandler {
_device_instance: u32,
supported_services: SupportedServices,
transaction_manager: TransactionManager,
service_processors: ServiceProcessors,
pub stats: ApplicationStatistics,
}
#[derive(Debug, Clone)]
pub struct SupportedServices {
pub confirmed: Vec<ConfirmedServiceChoice>,
pub unconfirmed: Vec<UnconfirmedServiceChoice>,
}
impl Default for SupportedServices {
fn default() -> Self {
Self {
confirmed: vec![
ConfirmedServiceChoice::ReadProperty,
ConfirmedServiceChoice::WriteProperty,
ConfirmedServiceChoice::ReadPropertyMultiple,
ConfirmedServiceChoice::SubscribeCOV,
],
unconfirmed: vec![
UnconfirmedServiceChoice::WhoIs,
UnconfirmedServiceChoice::IAm,
UnconfirmedServiceChoice::UnconfirmedEventNotification,
],
}
}
}
type ServiceProcessor = Box<dyn Fn(&[u8]) -> Result<Vec<u8>> + Send + Sync>;
type OptionalServiceProcessor = Box<dyn Fn(&[u8]) -> Result<Option<Vec<u8>>> + Send + Sync>;
#[derive(Default)]
struct ServiceProcessors {
read_property: Option<ServiceProcessor>,
write_property: Option<ServiceProcessor>,
who_is: Option<OptionalServiceProcessor>,
}
impl fmt::Debug for ServiceProcessors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ServiceProcessors")
.field("read_property", &self.read_property.is_some())
.field("write_property", &self.write_property.is_some())
.field("who_is", &self.who_is.is_some())
.finish()
}
}
impl ApplicationLayerHandler {
pub fn new(device_instance: u32) -> Self {
Self {
_device_instance: device_instance,
supported_services: SupportedServices::default(),
transaction_manager: TransactionManager::new(),
service_processors: ServiceProcessors::default(),
stats: ApplicationStatistics::default(),
}
}
pub fn process_apdu(&mut self, apdu: &Apdu, _source: &[u8]) -> Result<Option<Apdu>> {
self.stats.apdus_received += 1;
match apdu {
Apdu::ConfirmedRequest {
segmented,
more_follows,
segmented_response_accepted,
max_segments: _,
max_response_size: _,
invoke_id,
sequence_number: _,
proposed_window_size: _,
service_choice,
service_data,
} => {
let pdu_flags = PduFlags {
segmented: *segmented,
more_follows: *more_follows,
segmented_response_accepted: *segmented_response_accepted,
};
self.process_confirmed_request(pdu_flags, *invoke_id, *service_choice, service_data)
}
Apdu::UnconfirmedRequest {
service_choice,
service_data,
} => self.process_unconfirmed_request(*service_choice, service_data),
Apdu::SimpleAck {
invoke_id,
service_choice,
} => self.process_simple_ack(*invoke_id, *service_choice),
Apdu::ComplexAck {
segmented,
more_follows,
invoke_id,
sequence_number: _,
proposed_window_size: _,
service_choice,
service_data,
} => {
let pdu_flags = PduFlags {
segmented: *segmented,
more_follows: *more_follows,
segmented_response_accepted: false,
};
self.process_complex_ack(pdu_flags, *invoke_id, *service_choice, service_data)
}
Apdu::Error {
invoke_id,
service_choice,
error_class,
error_code,
} => self.process_error(*invoke_id, *service_choice, *error_class, *error_code),
Apdu::Reject {
invoke_id,
reject_reason,
} => self.process_reject(*invoke_id, *reject_reason),
Apdu::Abort {
server,
invoke_id,
abort_reason,
} => self.process_abort(*server, *invoke_id, *abort_reason),
_ => {
self.stats.unknown_apdus += 1;
Err(ApplicationError::UnsupportedApduType)
}
}
}
fn process_confirmed_request(
&mut self,
_pdu_flags: PduFlags,
invoke_id: u8,
service_choice: ConfirmedServiceChoice,
service_data: &[u8],
) -> Result<Option<Apdu>> {
self.stats.confirmed_requests += 1;
if !self.supported_services.confirmed.contains(&service_choice) {
return Ok(Some(Apdu::Reject {
invoke_id,
reject_reason: RejectReason::UnrecognizedService,
}));
}
match service_choice {
ConfirmedServiceChoice::ReadProperty => {
if let Some(ref processor) = self.service_processors.read_property {
match processor(service_data) {
Ok(response_data) => Ok(Some(Apdu::ComplexAck {
segmented: false,
more_follows: false,
invoke_id,
sequence_number: None,
proposed_window_size: None,
service_choice,
service_data: response_data,
})),
Err(_) => {
Ok(Some(Apdu::Error {
invoke_id,
service_choice,
error_class: 0, error_code: 0, }))
}
}
} else {
Ok(Some(Apdu::Abort {
server: true,
invoke_id,
abort_reason: AbortReason::Other as u8,
}))
}
}
_ => Ok(Some(Apdu::Reject {
invoke_id,
reject_reason: RejectReason::UnrecognizedService,
})),
}
}
fn process_unconfirmed_request(
&mut self,
service_choice: UnconfirmedServiceChoice,
service_data: &[u8],
) -> Result<Option<Apdu>> {
self.stats.unconfirmed_requests += 1;
if service_choice == UnconfirmedServiceChoice::WhoIs {
if let Some(ref processor) = self.service_processors.who_is {
if let Ok(Some(response_data)) = processor(service_data) {
return Ok(Some(Apdu::UnconfirmedRequest {
service_choice: UnconfirmedServiceChoice::IAm,
service_data: response_data,
}));
}
}
}
Ok(None)
}
fn process_simple_ack(&mut self, invoke_id: u8, _service_choice: u8) -> Result<Option<Apdu>> {
self.stats.simple_acks += 1;
self.transaction_manager.complete_transaction(invoke_id);
Ok(None)
}
fn process_complex_ack(
&mut self,
_pdu_flags: PduFlags,
invoke_id: u8,
_service_choice: ConfirmedServiceChoice,
_service_data: &[u8],
) -> Result<Option<Apdu>> {
self.stats.complex_acks += 1;
self.transaction_manager.complete_transaction(invoke_id);
Ok(None)
}
fn process_error(
&mut self,
invoke_id: u8,
_service_choice: ConfirmedServiceChoice,
error_class: u8,
error_code: u8,
) -> Result<Option<Apdu>> {
self.stats.errors += 1;
self.transaction_manager
.error_transaction(invoke_id, error_class, error_code);
Ok(None)
}
fn process_reject(
&mut self,
invoke_id: u8,
reject_reason: RejectReason,
) -> Result<Option<Apdu>> {
self.stats.rejects += 1;
self.transaction_manager
.reject_transaction(invoke_id, reject_reason);
Ok(None)
}
fn process_abort(
&mut self,
_server: bool,
invoke_id: u8,
abort_reason: u8,
) -> Result<Option<Apdu>> {
self.stats.aborts += 1;
self.transaction_manager
.abort_transaction(invoke_id, abort_reason);
Ok(None)
}
pub fn set_read_property_handler<F>(&mut self, handler: F)
where
F: Fn(&[u8]) -> Result<Vec<u8>> + Send + Sync + 'static,
{
self.service_processors.read_property = Some(Box::new(handler));
}
pub fn set_who_is_handler<F>(&mut self, handler: F)
where
F: Fn(&[u8]) -> Result<Option<Vec<u8>>> + Send + Sync + 'static,
{
self.service_processors.who_is = Some(Box::new(handler));
}
}
#[derive(Debug)]
pub struct TransactionManager {
transactions: Vec<Transaction>,
max_transactions: usize,
#[cfg(feature = "std")]
_timeout: Duration,
}
impl TransactionManager {
pub fn new() -> Self {
Self {
transactions: Vec::new(),
max_transactions: 255,
#[cfg(feature = "std")]
_timeout: Duration::from_secs(30),
}
}
pub fn start_transaction(&mut self, invoke_id: u8, service_choice: u8) -> Result<()> {
if self.transactions.len() >= self.max_transactions {
return Err(ApplicationError::TransactionError(
"Too many active transactions".to_string(),
));
}
if self
.transactions
.iter()
.any(|t| t.invoke_id == invoke_id && t.state == TransactionState::AwaitConfirmation)
{
return Err(ApplicationError::TransactionError(
"Duplicate invoke ID".to_string(),
));
}
self.transactions.push(Transaction {
invoke_id,
service: service_choice,
state: TransactionState::AwaitConfirmation,
timeout: Duration::from_secs(30),
retries: 0,
});
Ok(())
}
pub fn complete_transaction(&mut self, invoke_id: u8) {
if let Some(transaction) = self
.transactions
.iter_mut()
.find(|t| t.invoke_id == invoke_id)
{
transaction.state = TransactionState::Complete;
}
}
pub fn error_transaction(&mut self, invoke_id: u8, _error_class: u8, _error_code: u8) {
if let Some(transaction) = self
.transactions
.iter_mut()
.find(|t| t.invoke_id == invoke_id)
{
transaction.state = TransactionState::Complete;
}
}
pub fn reject_transaction(&mut self, invoke_id: u8, _reject_reason: RejectReason) {
if let Some(transaction) = self
.transactions
.iter_mut()
.find(|t| t.invoke_id == invoke_id)
{
transaction.state = TransactionState::Complete;
}
}
pub fn abort_transaction(&mut self, invoke_id: u8, _abort_reason: u8) {
if let Some(transaction) = self
.transactions
.iter_mut()
.find(|t| t.invoke_id == invoke_id)
{
transaction.state = TransactionState::Complete;
}
}
pub fn cleanup_completed(&mut self) {
self.transactions
.retain(|t| t.state != TransactionState::Complete);
}
pub fn active_count(&self) -> usize {
self.transactions
.iter()
.filter(|t| t.state != TransactionState::Complete)
.count()
}
}
impl Default for TransactionManager {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Default)]
pub struct ApplicationStatistics {
pub apdus_received: u64,
pub apdus_sent: u64,
pub confirmed_requests: u64,
pub unconfirmed_requests: u64,
pub simple_acks: u64,
pub complex_acks: u64,
pub errors: u64,
pub rejects: u64,
pub aborts: u64,
pub unknown_apdus: u64,
pub segmentation_errors: u64,
}
#[derive(Debug)]
pub struct ApplicationPriorityQueue {
high: Vec<QueuedMessage>,
normal: Vec<QueuedMessage>,
low: Vec<QueuedMessage>,
max_queue_size: usize,
}
#[derive(Debug)]
struct QueuedMessage {
apdu: Apdu,
destination: Vec<u8>,
#[cfg(feature = "std")]
_timestamp: std::time::Instant,
_retry_count: u8,
}
impl ApplicationPriorityQueue {
pub fn new(max_queue_size: usize) -> Self {
Self {
high: Vec::with_capacity(max_queue_size),
normal: Vec::with_capacity(max_queue_size),
low: Vec::with_capacity(max_queue_size),
max_queue_size,
}
}
pub fn enqueue(
&mut self,
apdu: Apdu,
destination: Vec<u8>,
priority: MessagePriority,
) -> Result<()> {
let queue = match priority {
MessagePriority::High => &mut self.high,
MessagePriority::Normal => &mut self.normal,
MessagePriority::Low => &mut self.low,
};
if queue.len() >= self.max_queue_size {
return Err(ApplicationError::TransactionError("Queue full".to_string()));
}
queue.push(QueuedMessage {
apdu,
destination,
#[cfg(feature = "std")]
_timestamp: std::time::Instant::now(),
_retry_count: 0,
});
Ok(())
}
pub fn dequeue(&mut self) -> Option<(Apdu, Vec<u8>)> {
if let Some(msg) = self.high.pop() {
return Some((msg.apdu, msg.destination));
}
if let Some(msg) = self.normal.pop() {
return Some((msg.apdu, msg.destination));
}
if let Some(msg) = self.low.pop() {
return Some((msg.apdu, msg.destination));
}
None
}
pub fn total_queued(&self) -> usize {
self.high.len() + self.normal.len() + self.low.len()
}
pub fn clear(&mut self) {
self.high.clear();
self.normal.clear();
self.low.clear();
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MessagePriority {
High,
Normal,
Low,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct PduFlags {
pub segmented: bool,
pub more_follows: bool,
pub segmented_response_accepted: bool,
}
#[derive(Debug, Clone)]
pub struct ApplicationConfig {
pub max_apdu_length: u16,
pub segmentation: Segmentation,
pub apdu_timeout: u16,
pub apdu_retries: u8,
pub max_segments: u8,
pub invoke_id_start: u8,
}
impl Default for ApplicationConfig {
fn default() -> Self {
Self {
max_apdu_length: 1476,
segmentation: Segmentation::Both,
apdu_timeout: 6000,
apdu_retries: 3,
max_segments: 64,
invoke_id_start: 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unconfirmed_request_encode_decode() {
let apdu = Apdu::UnconfirmedRequest {
service_choice: UnconfirmedServiceChoice::WhoIs, service_data: vec![0x08, 0x7B, 0x18, 0x7B], };
let encoded = apdu.encode();
let decoded = Apdu::decode(&encoded).unwrap();
match decoded {
Apdu::UnconfirmedRequest {
service_choice,
service_data,
} => {
assert_eq!(service_choice, UnconfirmedServiceChoice::WhoIs);
assert_eq!(service_data, vec![0x08, 0x7B, 0x18, 0x7B]);
}
_ => panic!("Expected UnconfirmedRequest"),
}
}
#[test]
fn test_simple_ack_encode_decode() {
let apdu = Apdu::SimpleAck {
invoke_id: 42,
service_choice: 12, };
let encoded = apdu.encode();
let decoded = Apdu::decode(&encoded).unwrap();
match decoded {
Apdu::SimpleAck {
invoke_id,
service_choice,
} => {
assert_eq!(invoke_id, 42);
assert_eq!(service_choice, 12);
}
_ => panic!("Expected SimpleAck"),
}
}
#[test]
fn test_confirmed_request_encode_decode() {
let apdu = Apdu::ConfirmedRequest {
segmented: false,
more_follows: false,
segmented_response_accepted: true,
max_segments: MaxSegments::Unspecified,
max_response_size: MaxApduSize::Up1476,
invoke_id: 123,
sequence_number: None,
proposed_window_size: None,
service_choice: ConfirmedServiceChoice::ReadProperty, service_data: vec![0x0C, 0x02, 0x00, 0x00, 0x08, 0x19, 0x55],
};
let encoded = apdu.encode();
let decoded = Apdu::decode(&encoded).unwrap();
match decoded {
Apdu::ConfirmedRequest {
invoke_id,
service_choice,
segmented_response_accepted,
..
} => {
assert_eq!(invoke_id, 123);
assert_eq!(service_choice, ConfirmedServiceChoice::ReadProperty);
assert!(segmented_response_accepted);
}
_ => panic!("Expected ConfirmedRequest"),
}
}
#[test]
fn test_invoke_id_manager() {
let mut manager = InvokeIdManager::new();
let id1 = manager.next_id().unwrap();
let id2 = manager.next_id().unwrap();
let id3 = manager.next_id().unwrap();
assert_ne!(id1, id2);
assert_ne!(id2, id3);
assert_ne!(id1, id3);
assert!(manager.is_active(id1));
assert!(manager.is_active(id2));
assert!(manager.is_active(id3));
manager.release_id(id2);
assert!(!manager.is_active(id2));
assert!(manager.is_active(id1));
assert!(manager.is_active(id3));
}
#[test]
fn test_max_apdu_size() {
assert_eq!(MaxApduSize::Up50.size(), 50);
assert_eq!(MaxApduSize::Up128.size(), 128);
assert_eq!(MaxApduSize::Up1476.size(), 1476);
}
#[test]
fn test_segmentation_info() {
let seg_info = SegmentationInfo::new(
true, true, 64, 1476, 5, 10, );
assert!(seg_info.more_follows);
assert!(seg_info.segmented_response_accepted);
assert_eq!(seg_info.max_segments_accepted, 64);
assert_eq!(seg_info.max_apdu_length_accepted, 1476);
assert_eq!(seg_info.sequence_number, 5);
assert_eq!(seg_info.proposed_window_size, 10);
assert!(!seg_info.is_first_segment());
assert!(!seg_info.is_last_segment());
assert_eq!(seg_info.max_segment_size(), 1470);
let first_seg = SegmentationInfo::new(false, true, 32, 1024, 0, 8);
assert!(first_seg.is_first_segment());
assert!(first_seg.is_last_segment());
let last_seg = SegmentationInfo::new(false, true, 16, 480, 3, 5);
assert!(!last_seg.is_first_segment());
assert!(last_seg.is_last_segment());
}
#[test]
fn test_segment_reassembly_buffer() {
let mut buffer = SegmentReassemblyBuffer::new(42, 1024);
assert_eq!(buffer.invoke_id, 42);
assert_eq!(buffer.max_apdu_length, 1024);
assert!(!buffer.is_complete());
assert_eq!(buffer.missing_segments(), Vec::<u8>::new());
buffer.add_segment(0, vec![1, 2, 3], false).unwrap();
buffer.add_segment(1, vec![4, 5, 6], false).unwrap();
buffer.add_segment(2, vec![7, 8, 9], true).unwrap();
assert!(buffer.is_complete());
assert_eq!(buffer.total_segments, Some(3));
let reassembled = buffer.reassemble().unwrap();
assert_eq!(reassembled, vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
let mut incomplete_buffer = SegmentReassemblyBuffer::new(43, 1024);
incomplete_buffer.add_segment(0, vec![1, 2], false).unwrap();
incomplete_buffer.add_segment(2, vec![5, 6], true).unwrap();
assert!(!incomplete_buffer.is_complete());
assert_eq!(incomplete_buffer.missing_segments(), vec![1]);
}
#[test]
fn test_segmentation_manager() {
let mut manager = SegmentationManager::new();
assert_eq!(manager.active_reassemblies(), 0);
let large_data = vec![0u8; 100]; let segments = manager.segment_message(&large_data, 30, 10).unwrap();
assert_eq!(segments.len(), 4); assert_eq!(segments[0].len(), 30);
assert_eq!(segments[1].len(), 30);
assert_eq!(segments[2].len(), 30);
assert_eq!(segments[3].len(), 10);
let invoke_id = 100;
let max_apdu = 1024;
let result1 = manager
.process_segment(invoke_id, 0, vec![1, 2, 3], true, max_apdu)
.unwrap();
assert!(result1.is_none()); assert_eq!(manager.active_reassemblies(), 1);
let result2 = manager
.process_segment(invoke_id, 1, vec![4, 5, 6], false, max_apdu)
.unwrap();
assert!(result2.is_some()); assert_eq!(result2.unwrap(), vec![1, 2, 3, 4, 5, 6]);
assert_eq!(manager.active_reassemblies(), 0);
manager
.process_segment(200, 0, vec![10, 20], true, max_apdu)
.unwrap();
manager
.process_segment(200, 2, vec![50, 60], false, max_apdu)
.unwrap();
let missing = manager.get_missing_segments(200);
assert_eq!(missing, vec![1]);
}
#[test]
fn test_segmentation_error_cases() {
let manager = SegmentationManager::new();
let huge_data = vec![0u8; 1000];
let result = manager.segment_message(&huge_data, 100, 5); assert!(result.is_err());
match result.unwrap_err() {
ApplicationError::SegmentationError(msg) => {
assert!(msg.contains("too large"));
}
_ => panic!("Expected SegmentationError"),
}
let mut buffer = SegmentReassemblyBuffer::new(1, 100);
buffer.add_segment(0, vec![1, 2], false).unwrap();
let result = buffer.reassemble();
assert!(result.is_err());
match result.unwrap_err() {
ApplicationError::SegmentationError(msg) => {
assert!(msg.contains("Incomplete"));
}
_ => panic!("Expected SegmentationError"),
}
}
#[test]
fn test_segmentation_duplicate_handling() {
let mut buffer = SegmentReassemblyBuffer::new(1, 1024);
buffer.add_segment(0, vec![1, 2, 3], false).unwrap();
assert_eq!(buffer.segments.len(), 1);
buffer.add_segment(0, vec![4, 5, 6], false).unwrap();
assert_eq!(buffer.segments.len(), 1);
assert_eq!(buffer.segments[0].1, vec![1, 2, 3]);
buffer.add_segment(1, vec![7, 8, 9], true).unwrap();
assert_eq!(buffer.segments.len(), 2);
assert!(buffer.is_complete());
let reassembled = buffer.reassemble().unwrap();
assert_eq!(reassembled, vec![1, 2, 3, 7, 8, 9]);
}
}