use tracing::debug;
use super::HANDSHAKE_BANNER;
use crate::error::{HdcError, Result};
#[repr(C)]
#[derive(Debug, Clone)]
pub struct ChannelHandShake {
pub banner: [u8; 12],
pub channel_id_or_key: [u8; 32],
pub version: [u8; 64],
}
impl ChannelHandShake {
pub const SIZE: usize = 12 + 32 + 64;
pub const SIZE_WITHOUT_VERSION: usize = 12 + 32;
const BANNER_FEATURE_TAG_OFFSET: usize = 7;
const HUGE_BUF_TAG: u8 = b'1';
pub fn from_bytes(data: &[u8]) -> Result<Self> {
if data.len() < Self::SIZE_WITHOUT_VERSION {
return Err(HdcError::Protocol(format!(
"Handshake data too short: expected at least {}, got {}",
Self::SIZE_WITHOUT_VERSION,
data.len()
)));
}
let mut handshake = ChannelHandShake {
banner: [0; 12],
channel_id_or_key: [0; 32],
version: [0; 64],
};
handshake.banner.copy_from_slice(&data[0..12]);
handshake.channel_id_or_key.copy_from_slice(&data[12..44]);
if data.len() >= Self::SIZE {
handshake.version.copy_from_slice(&data[44..108]);
} else {
debug!(
"Received handshake without version field ({} bytes)",
data.len()
);
}
Ok(handshake)
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(Self::SIZE);
bytes.extend_from_slice(&self.banner);
bytes.extend_from_slice(&self.channel_id_or_key);
bytes.extend_from_slice(&self.version);
bytes
}
pub fn to_bytes_without_version(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(Self::SIZE_WITHOUT_VERSION);
bytes.extend_from_slice(&self.banner);
bytes.extend_from_slice(&self.channel_id_or_key);
bytes
}
pub fn verify_banner(&self) -> Result<()> {
if !self.banner.starts_with(HANDSHAKE_BANNER) {
return Err(HdcError::InvalidBanner(self.banner.to_vec()));
}
Ok(())
}
pub fn get_channel_id(&self) -> u32 {
u32::from_be_bytes([
self.channel_id_or_key[0],
self.channel_id_or_key[1],
self.channel_id_or_key[2],
self.channel_id_or_key[3],
])
}
pub fn set_channel_id(&mut self, channel_id: u32) {
let bytes = channel_id.to_be_bytes();
self.channel_id_or_key[0..4].copy_from_slice(&bytes);
}
pub fn set_connect_key(&mut self, connect_key: &str) {
self.channel_id_or_key.fill(0);
let key_bytes = connect_key.as_bytes();
let copy_len = key_bytes.len().min(32);
self.channel_id_or_key[0..copy_len].copy_from_slice(&key_bytes[0..copy_len]);
}
pub fn get_connect_key(&self) -> String {
let end = self
.channel_id_or_key
.iter()
.position(|&b| b == 0)
.unwrap_or(32);
String::from_utf8_lossy(&self.channel_id_or_key[0..end]).to_string()
}
pub fn is_stable_buf(&self) -> bool {
if self.banner.len() > Self::BANNER_FEATURE_TAG_OFFSET {
self.banner[Self::BANNER_FEATURE_TAG_OFFSET] != Self::HUGE_BUF_TAG
} else {
true
}
}
pub fn get_version(&self) -> String {
let end = self.version.iter().position(|&b| b == 0).unwrap_or(64);
String::from_utf8_lossy(&self.version[0..end]).to_string()
}
pub fn set_version(&mut self, version: &str) {
self.version.fill(0);
let version_bytes = version.as_bytes();
let copy_len = version_bytes.len().min(64);
self.version[0..copy_len].copy_from_slice(&version_bytes[0..copy_len]);
}
}
impl Default for ChannelHandShake {
fn default() -> Self {
Self {
banner: [0; 12],
channel_id_or_key: [0; 32],
version: [0; 64],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_handshake_size() {
assert_eq!(ChannelHandShake::SIZE, 108);
assert_eq!(ChannelHandShake::SIZE_WITHOUT_VERSION, 44);
assert_eq!(std::mem::size_of::<ChannelHandShake>(), 108);
}
#[test]
fn test_handshake_from_bytes_short() {
let mut data = vec![0u8; 44];
data[..8].copy_from_slice(b"OHOS HDC");
data[12..16].copy_from_slice(&0x12345678u32.to_be_bytes());
let hs = ChannelHandShake::from_bytes(&data).unwrap();
assert_eq!(hs.get_channel_id(), 0x12345678);
assert_eq!(hs.get_version(), "");
}
#[test]
fn test_handshake_from_bytes_full() {
let mut data = vec![0u8; 108];
data[..8].copy_from_slice(b"OHOS HDC");
data[12..16].copy_from_slice(&0x12345678u32.to_be_bytes());
data[44..49].copy_from_slice(b"3.2.0");
let hs = ChannelHandShake::from_bytes(&data).unwrap();
assert_eq!(hs.get_channel_id(), 0x12345678);
assert_eq!(hs.get_version(), "3.2.0");
}
#[test]
fn test_channel_id() {
let mut hs = ChannelHandShake::default();
hs.set_channel_id(0x12345678);
assert_eq!(hs.get_channel_id(), 0x12345678);
}
#[test]
fn test_connect_key() {
let mut hs = ChannelHandShake::default();
hs.set_connect_key("test-device-001");
assert_eq!(hs.get_connect_key(), "test-device-001");
}
#[test]
fn test_version() {
let mut hs = ChannelHandShake::default();
hs.set_version("3.2.0");
assert_eq!(hs.get_version(), "3.2.0");
}
#[test]
fn test_to_bytes_without_version() {
let mut hs = ChannelHandShake::default();
hs.banner[..8].copy_from_slice(b"OHOS HDC");
hs.set_channel_id(0x12345678);
let bytes = hs.to_bytes_without_version();
assert_eq!(bytes.len(), 44);
assert_eq!(&bytes[..8], b"OHOS HDC");
}
}