use std::io::Cursor;
use byteorder::{LittleEndian, ReadBytesExt};
use crate::{Error, MIN_REQUEST_LENGTH, RFC_REQUEST_FRAME_BYTES, RtMessage, Tag};
use crate::version::Version;
pub fn nonce_from_request(buf: &[u8], num_bytes: usize) -> Result<(Vec<u8>, Version), Error> {
if num_bytes < MIN_REQUEST_LENGTH as usize {
return Err(Error::RequestTooShort);
}
if is_classic_request(buf) {
return nonce_from_classic_request(&buf[..num_bytes]);
} else {
return nonce_from_rfc_request(&buf[..num_bytes]);
}
}
fn is_classic_request(buf: &[u8]) -> bool {
return &buf[0..8] != RFC_REQUEST_FRAME_BYTES;
}
fn nonce_from_classic_request(buf: &[u8]) -> Result<(Vec<u8>, Version), Error> {
let msg = RtMessage::from_bytes(buf)?;
match msg.get_field(Tag::NONC) {
Some(nonce) => Ok((nonce.to_vec(), Version::Classic)),
None => Err(Error::InvalidRequest),
}
}
fn nonce_from_rfc_request(buf: &[u8]) -> Result<(Vec<u8>, Version), Error> {
let mut cur = Cursor::new(&buf[8..12]);
let reported_len = cur.read_u32::<LittleEndian>()?;
let actual_len = (buf.len() - 12) as u32;
if reported_len != actual_len {
return Err(Error::LengthMismatch(reported_len, actual_len));
}
let msg = RtMessage::from_bytes(&buf[12..])?;
let version = get_supported_version(&msg);
if version.is_none() {
return Err(Error::NoCompatibleVersion);
}
match msg.get_field(Tag::NONC) {
Some(nonce) => Ok((nonce.to_vec(), version.unwrap())),
None => Err(Error::InvalidRequest),
}
}
fn get_supported_version(msg: &RtMessage) -> Option<Version> {
const SUPPORTED_VERSIONS: &[Version] = &[Version::RfcDraft8, Version::Rfc];
if let Some(tag_bytes) = msg.get_field(Tag::VER) {
for found_ver_bytes in tag_bytes.chunks(4) {
for ver in SUPPORTED_VERSIONS {
if ver.wire_bytes() == found_ver_bytes {
return Some(*ver);
}
}
}
}
None
}