use std::collections::HashMap;
use std::sync::Arc;
use crate::apdu::types::{
AbortReason, ConfirmedService, ErrorClass, ErrorCode, RejectReason, UnconfirmedService,
};
use crate::object::ObjectRegistry;
pub struct ServiceContext {
pub objects: Arc<ObjectRegistry>,
pub device_instance: u32,
pub invoke_id: Option<u8>,
pub max_apdu_length: u16,
pub source_address: Option<std::net::SocketAddr>,
}
impl ServiceContext {
pub fn new(objects: Arc<ObjectRegistry>, device_instance: u32) -> Self {
Self {
objects,
device_instance,
invoke_id: None,
max_apdu_length: 1476,
source_address: None,
}
}
pub fn with_invoke_id(mut self, invoke_id: u8) -> Self {
self.invoke_id = Some(invoke_id);
self
}
pub fn with_source_address(mut self, addr: std::net::SocketAddr) -> Self {
self.source_address = Some(addr);
self
}
}
#[derive(Debug)]
pub enum ServiceResult {
SimpleAck,
ComplexAck(Vec<u8>),
Error {
error_class: ErrorClass,
error_code: ErrorCode,
},
Reject(u8),
Abort(AbortReason),
NoResponse,
Broadcast(Vec<u8>),
}
impl ServiceResult {
pub fn unknown_property() -> Self {
Self::Error {
error_class: ErrorClass::Property,
error_code: ErrorCode::UnknownProperty,
}
}
pub fn unknown_object() -> Self {
Self::Error {
error_class: ErrorClass::Object,
error_code: ErrorCode::UnknownObject,
}
}
pub fn unrecognized_service() -> Self {
Self::Reject(RejectReason::UnrecognizedService as u8)
}
pub fn missing_required_parameter() -> Self {
Self::Reject(RejectReason::MissingRequiredParameter as u8)
}
pub fn invalid_tag() -> Self {
Self::Reject(RejectReason::InvalidTag as u8)
}
pub fn too_many_arguments() -> Self {
Self::Reject(RejectReason::TooManyArguments as u8)
}
pub fn segmentation_not_supported() -> Self {
Self::Abort(AbortReason::SegmentationNotSupported)
}
pub fn buffer_overflow() -> Self {
Self::Abort(AbortReason::BufferOverflow)
}
pub fn out_of_resources() -> Self {
Self::Abort(AbortReason::OutOfResources)
}
pub fn apdu_too_long() -> Self {
Self::Abort(AbortReason::ApduTooLong)
}
pub fn write_access_denied() -> Self {
Self::Error {
error_class: ErrorClass::Property,
error_code: ErrorCode::WriteAccessDenied,
}
}
pub fn service_request_denied() -> Self {
Self::Error {
error_class: ErrorClass::Services,
error_code: ErrorCode::ServiceRequestDenied,
}
}
pub fn communication_disabled() -> Self {
Self::Error {
error_class: ErrorClass::Device,
error_code: ErrorCode::CommunicationDisabled,
}
}
}
pub trait ConfirmedServiceHandler: Send + Sync {
fn service_choice(&self) -> ConfirmedService;
fn handle(&self, data: &[u8], ctx: &ServiceContext) -> ServiceResult;
fn name(&self) -> &'static str;
fn min_data_length(&self) -> usize {
0
}
}
pub trait UnconfirmedServiceHandler: Send + Sync {
fn service_choice(&self) -> UnconfirmedService;
fn handle(&self, data: &[u8], ctx: &ServiceContext) -> ServiceResult;
fn name(&self) -> &'static str;
}
pub struct ServiceRegistry {
confirmed_handlers: HashMap<u8, Arc<dyn ConfirmedServiceHandler>>,
unconfirmed_handlers: HashMap<u8, Arc<dyn UnconfirmedServiceHandler>>,
}
impl ServiceRegistry {
pub fn new() -> Self {
Self {
confirmed_handlers: HashMap::new(),
unconfirmed_handlers: HashMap::new(),
}
}
pub fn register_confirmed(
&mut self,
handler: Arc<dyn ConfirmedServiceHandler>,
) -> Option<Arc<dyn ConfirmedServiceHandler>> {
let service = handler.service_choice() as u8;
self.confirmed_handlers.insert(service, handler)
}
pub fn register_unconfirmed(
&mut self,
handler: Arc<dyn UnconfirmedServiceHandler>,
) -> Option<Arc<dyn UnconfirmedServiceHandler>> {
let service = handler.service_choice() as u8;
self.unconfirmed_handlers.insert(service, handler)
}
pub fn get_confirmed(&self, service: u8) -> Option<&Arc<dyn ConfirmedServiceHandler>> {
self.confirmed_handlers.get(&service)
}
pub fn get_unconfirmed(&self, service: u8) -> Option<&Arc<dyn UnconfirmedServiceHandler>> {
self.unconfirmed_handlers.get(&service)
}
pub fn has_confirmed(&self, service: u8) -> bool {
self.confirmed_handlers.contains_key(&service)
}
pub fn has_unconfirmed(&self, service: u8) -> bool {
self.unconfirmed_handlers.contains_key(&service)
}
pub fn dispatch_confirmed(
&self,
service: u8,
data: &[u8],
ctx: &ServiceContext,
) -> ServiceResult {
match self.confirmed_handlers.get(&service) {
Some(handler) => {
if data.len() < handler.min_data_length() {
return ServiceResult::missing_required_parameter();
}
handler.handle(data, ctx)
}
None => ServiceResult::unrecognized_service(),
}
}
pub fn dispatch_unconfirmed(
&self,
service: u8,
data: &[u8],
ctx: &ServiceContext,
) -> ServiceResult {
match self.unconfirmed_handlers.get(&service) {
Some(handler) => handler.handle(data, ctx),
None => ServiceResult::NoResponse,
}
}
pub fn supported_confirmed_services(&self) -> Vec<u8> {
self.confirmed_handlers.keys().copied().collect()
}
pub fn supported_unconfirmed_services(&self) -> Vec<u8> {
self.unconfirmed_handlers.keys().copied().collect()
}
}
impl Default for ServiceRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockReadPropertyHandler;
impl ConfirmedServiceHandler for MockReadPropertyHandler {
fn service_choice(&self) -> ConfirmedService {
ConfirmedService::ReadProperty
}
fn handle(&self, _data: &[u8], _ctx: &ServiceContext) -> ServiceResult {
ServiceResult::ComplexAck(vec![0x0C, 0x00])
}
fn name(&self) -> &'static str {
"ReadProperty"
}
}
#[test]
fn test_service_registry() {
let mut registry = ServiceRegistry::new();
let handler = Arc::new(MockReadPropertyHandler);
registry.register_confirmed(handler);
assert!(registry.has_confirmed(ConfirmedService::ReadProperty as u8));
assert!(!registry.has_confirmed(ConfirmedService::WriteProperty as u8));
}
}