use crate::error::ZmqError;
use bytes::{BufMut, BytesMut};
use std::convert::TryInto;
pub const GREETING_PREFIX: &[u8; 10] =
&[0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x7F];
pub const GREETING_VERSION_MAJOR: u8 = 3;
pub const GREETING_VERSION_MINOR: u8 = 0; pub const GREETING_VERSION_MAJOR_BYTE: u8 = 0x03; pub const GREETING_VERSION_MINOR_BYTE: u8 = 0x00;
pub const GREETING_LENGTH: usize = 64;
pub const MECHANISM_OFFSET: usize = GREETING_PREFIX.len() + 2; pub const MECHANISM_LENGTH: usize = 20;
pub const AS_SERVER_OFFSET: usize = MECHANISM_OFFSET + MECHANISM_LENGTH;
const PADDING_OFFSET: usize = AS_SERVER_OFFSET + 1; const PADDING_LENGTH: usize = GREETING_LENGTH - PADDING_OFFSET;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ZmtpGreeting {
pub version: (u8, u8),
pub mechanism: [u8; MECHANISM_LENGTH], pub as_server: bool,
}
impl ZmtpGreeting {
pub fn encode(mechanism: &[u8; MECHANISM_LENGTH], as_server: bool, buffer: &mut BytesMut) {
buffer.reserve(GREETING_LENGTH);
buffer.put_slice(GREETING_PREFIX);
buffer.put_u8(GREETING_VERSION_MAJOR_BYTE); buffer.put_u8(GREETING_VERSION_MINOR_BYTE); buffer.put_slice(mechanism);
buffer.put_u8(as_server as u8);
let current_len = GREETING_PREFIX.len() + 2 + MECHANISM_LENGTH + 1;
let padding_len = GREETING_LENGTH - current_len;
if padding_len > 0 {
buffer.put_bytes(0, padding_len);
}
debug_assert_eq!(buffer.len(), GREETING_LENGTH);
}
pub fn decode(buffer: &mut BytesMut) -> Result<Option<Self>, ZmqError> {
if buffer.len() < GREETING_LENGTH {
return Ok(None); }
let data = buffer.split_to(GREETING_LENGTH);
if data[0] != 0xFF || data[9] != 0x7F {
tracing::error!(
"Invalid ZMTP greeting signature markers. Expected start=255 and end=127. Got start={}, end={}",
data[0],
data[9]
);
tracing::debug!(
"Full received greeting prefix (10 bytes): {:?}",
&data[..10]
);
return Err(ZmqError::ProtocolViolation(
"Invalid greeting signature markers".into(),
));
}
for i in 0..PADDING_LENGTH {
if data[PADDING_OFFSET + i] != 0x00 {
tracing::error!(
"Invalid ZMTP greeting: Non-zero byte found in reserved padding area at offset {}. Expected 0x00, Got: {:#04x}",
PADDING_OFFSET + i,
data[PADDING_OFFSET + i]
);
return Err(ZmqError::ProtocolViolation(
"Non-zero byte in greeting padding".into(),
));
}
}
let major_version = data[GREETING_PREFIX.len()]; let minor_version = data[GREETING_PREFIX.len() + 1];
if major_version != GREETING_VERSION_MAJOR_BYTE {
return Err(ZmqError::ProtocolViolation(format!(
"Unsupported ZMTP major version {}.{}",
major_version, minor_version
)));
}
let version = (major_version, minor_version);
let mechanism_slice = &data[MECHANISM_OFFSET..MECHANISM_OFFSET + MECHANISM_LENGTH];
let mechanism: [u8; MECHANISM_LENGTH] = mechanism_slice.try_into().unwrap();
let as_server_byte = data[AS_SERVER_OFFSET];
let as_server = match as_server_byte {
0x00 => false,
0x01 => true,
_ => return Err(ZmqError::ProtocolViolation("Invalid as-server flag".into())),
};
tracing::debug!(?version, ?mechanism, as_server, "Parsed ZMTP Greeting");
Ok(Some(Self {
version,
mechanism,
as_server,
}))
}
pub fn mechanism_name(&self) -> &str {
let first_null = self
.mechanism
.iter()
.position(|&b| b == 0)
.unwrap_or(MECHANISM_LENGTH);
std::str::from_utf8(&self.mechanism[..first_null]).unwrap_or("<invalid_utf8>")
}
}