umsh_ulcp_device/
bridge.rs

1//! Saved device-domain configuration for one autonomous bridge connection.
2use heapless::String;
3use umsh_ulcp::{
4    Status,
5    bridge::{DEFAULT_PORT, HOST_MAX, valid_host},
6    ids::prop,
7};
8
9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct BridgeConfig {
11    pub enabled: bool,
12    pub host: String<HOST_MAX>,
13    pub port: u16,
14    pub server_key: Option<[u8; 32]>,
15}
16
17impl Default for BridgeConfig {
18    fn default() -> Self {
19        Self {
20            enabled: false,
21            host: String::new(),
22            port: DEFAULT_PORT,
23            server_key: None,
24        }
25    }
26}
27
28impl BridgeConfig {
29    pub fn configured(&self) -> bool {
30        !self.host.is_empty() && self.server_key.is_some()
31    }
32
33    pub fn set(&mut self, key: u32, value: &[u8]) -> Result<(), Status> {
34        match key {
35            prop::BRIDGE_ENABLED => {
36                self.enabled = match value {
37                    [0] => false,
38                    [1] => true,
39                    _ => return Err(Status::INVALID_ARGUMENT),
40                }
41            }
42            prop::BRIDGE_HOST => {
43                let bytes = value.strip_suffix(&[0]).ok_or(Status::INVALID_ARGUMENT)?;
44                let host = core::str::from_utf8(bytes).map_err(|_| Status::INVALID_ARGUMENT)?;
45                if !valid_host(host) {
46                    return Err(Status::INVALID_ARGUMENT);
47                }
48                self.host = String::try_from(host).map_err(|_| Status::NOMEM)?;
49            }
50            prop::BRIDGE_PORT => {
51                let port =
52                    u16::from_le_bytes(value.try_into().map_err(|_| Status::INVALID_ARGUMENT)?);
53                if port == 0 {
54                    return Err(Status::INVALID_ARGUMENT);
55                }
56                self.port = port;
57            }
58            prop::BRIDGE_SERVER_KEY => {
59                self.server_key = if value.is_empty() {
60                    None
61                } else {
62                    let bytes: [u8; 32] = value.try_into().map_err(|_| Status::INVALID_ARGUMENT)?;
63                    let key = ed25519_dalek::VerifyingKey::from_bytes(&bytes)
64                        .map_err(|_| Status::INVALID_ARGUMENT)?;
65                    if key.is_weak() || key.to_edwards().compress().to_bytes() != bytes {
66                        return Err(Status::INVALID_ARGUMENT);
67                    }
68                    Some(bytes)
69                };
70            }
71            _ => return Err(Status::PROP_NOT_FOUND),
72        }
73        Ok(())
74    }
75
76    pub fn encode(&self, key: u32, out: &mut [u8]) -> Option<usize> {
77        let bytes: &[u8] = match key {
78            prop::BRIDGE_ENABLED => &[self.enabled as u8],
79            prop::BRIDGE_PORT => &self.port.to_le_bytes(),
80            prop::BRIDGE_SERVER_KEY => self.server_key.as_ref().map_or(&[], |k| k.as_slice()),
81            prop::BRIDGE_HOST => {
82                let len = self.host.len();
83                out.get_mut(..len + 1)?[..len].copy_from_slice(self.host.as_bytes());
84                out[len] = 0;
85                return Some(len + 1);
86            }
87            _ => return None,
88        };
89        out.get_mut(..bytes.len())?.copy_from_slice(bytes);
90        Some(bytes.len())
91    }
92}