use std::io::Cursor;
use byteorder::{LittleEndian, ReadBytesExt};
use crate::version::Version;
use crate::{Error, RtMessage, Tag, MAX_REQUEST_LENGTH, MIN_REQUEST_LENGTH, REQUEST_FRAMING_BYTES};
pub fn nonce_from_request(
buf: &[u8],
num_bytes: usize,
expected_srv: &[u8],
) -> Result<(Vec<u8>, Version), Error> {
if num_bytes < MIN_REQUEST_LENGTH {
return Err(Error::RequestTooShort);
} else if num_bytes > MAX_REQUEST_LENGTH {
return Err(Error::RequestTooLarge);
}
if is_rfc_request(buf) {
nonce_from_rfc_request(&buf[..num_bytes], expected_srv)
} else {
nonce_from_classic_request(&buf[..num_bytes])
}
}
fn is_rfc_request(buf: &[u8]) -> bool {
&buf[0..8] == REQUEST_FRAMING_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::Google)),
None => Err(Error::InvalidRequest),
}
}
fn nonce_from_rfc_request(buf: &[u8], expected_srv: &[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);
}
if let Some(request_srv) = msg.get_field(Tag::SRV) {
if request_srv != expected_srv {
return Err(Error::SrvMismatch);
}
}
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::RfcDraft13];
const ITERATION_LIMIT: usize = 4;
if let Some(tag_bytes) = msg.get_field(Tag::VER) {
for found_ver_bytes in tag_bytes.chunks(4).take(ITERATION_LIMIT) {
for supported_ver in SUPPORTED_VERSIONS {
if supported_ver.wire_bytes() == found_ver_bytes {
return Some(*supported_ver);
}
}
}
}
None
}