use crate::codec::ZmtpError;
use bytes::Bytes;
pub const GREETING_SIZE: usize = 64;
const SIGNATURE_HEAD: u8 = 0xFF;
const SIGNATURE_TAIL: u8 = 0x7F;
#[derive(Debug, Clone)]
pub struct ZmtpGreeting {
pub mechanism: [u8; 20],
#[allow(dead_code)]
pub as_server: bool,
}
impl ZmtpGreeting {
pub fn parse(src: &Bytes) -> crate::codec::Result<Self> {
if src.len() < GREETING_SIZE {
return Err(ZmtpError::Incomplete);
}
if src.len() != GREETING_SIZE {
return Err(ZmtpError::Protocol);
}
if src[0] != SIGNATURE_HEAD || src[9] != SIGNATURE_TAIL {
return Err(ZmtpError::Protocol);
}
let major = src[10];
if major < 3 {
return Err(ZmtpError::Protocol);
}
let mut mechanism = [0u8; 20];
mechanism.copy_from_slice(&src[12..32]);
let mech_end = mechanism.iter().position(|&b| b == 0).unwrap_or(20);
if mech_end == 0
|| !mechanism[..mech_end]
.iter()
.all(|b| b.is_ascii_uppercase() || b.is_ascii_digit())
|| mechanism[mech_end..].iter().any(|&b| b != 0)
{
return Err(ZmtpError::Protocol);
}
if src[32] > 1 {
return Err(ZmtpError::Protocol);
}
let as_server = src[32] != 0;
Ok(Self {
mechanism,
as_server,
})
}
pub fn mechanism_str(&self) -> &str {
let end = self.mechanism.iter().position(|&b| b == 0).unwrap_or(20);
std::str::from_utf8(&self.mechanism[..end]).unwrap_or("UNKNOWN")
}
}
#[cfg(test)]
mod tests {
use super::*;
fn valid_greeting() -> Bytes {
let mut greeting = [0u8; GREETING_SIZE];
greeting[0] = SIGNATURE_HEAD;
greeting[9] = SIGNATURE_TAIL;
greeting[10] = 3;
greeting[11] = 1;
greeting[12..16].copy_from_slice(b"NULL");
Bytes::copy_from_slice(&greeting)
}
#[test]
fn parse_rejects_trailing_bytes_after_fixed_greeting() {
let mut greeting = valid_greeting().to_vec();
greeting.push(0);
assert!(matches!(
ZmtpGreeting::parse(&Bytes::from(greeting)),
Err(ZmtpError::Protocol)
));
}
#[test]
fn parse_rejects_invalid_as_server_flag() {
let mut greeting = valid_greeting().to_vec();
greeting[32] = 2;
assert!(matches!(
ZmtpGreeting::parse(&Bytes::from(greeting)),
Err(ZmtpError::Protocol)
));
}
#[test]
fn parse_rejects_invalid_security_mechanism_characters() {
let mut greeting = valid_greeting().to_vec();
greeting[12..18].copy_from_slice(b"BAD ME");
assert!(matches!(
ZmtpGreeting::parse(&Bytes::from(greeting)),
Err(ZmtpError::Protocol)
));
}
#[test]
fn parse_rejects_nonzero_mechanism_padding() {
let mut greeting = valid_greeting().to_vec();
greeting[17..22].copy_from_slice(b"CURVE");
assert!(matches!(
ZmtpGreeting::parse(&Bytes::from(greeting)),
Err(ZmtpError::Protocol)
));
}
}