use super::{Drep, RpcError};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NdrRequest {
pub args_max: u32,
pub args_len: u32,
pub max_count: u32,
pub offset: u32,
pub actual_count: u32,
}
impl NdrRequest {
pub const LEN: usize = 20;
pub fn parse(buf: &[u8], drep: Drep) -> Result<(NdrRequest, &[u8]), RpcError> {
if buf.len() < Self::LEN {
return Err(RpcError::TooShort {
need: Self::LEN,
have: buf.len(),
});
}
let args_max = drep.u32(&buf[0..4]);
let args_len = drep.u32(&buf[4..8]);
let max_count = drep.u32(&buf[8..12]);
let offset = drep.u32(&buf[12..16]);
let actual_count = drep.u32(&buf[16..20]);
if args_len != actual_count {
return Err(RpcError::NdrMismatch("args_len != actual_count"));
}
if offset != 0 {
return Err(RpcError::NdrMismatch("array offset must be 0"));
}
let available = buf.len() - Self::LEN;
if actual_count as usize > available {
return Err(RpcError::NdrMismatch("actual_count overruns buffer"));
}
let n = NdrRequest {
args_max,
args_len,
max_count,
offset,
actual_count,
};
let blocks = &buf[Self::LEN..Self::LEN + actual_count as usize];
Ok((n, blocks))
}
pub fn for_blocks(args_max: u32, blocks_len: u32) -> NdrRequest {
NdrRequest {
args_max,
args_len: blocks_len,
max_count: args_max,
offset: 0,
actual_count: blocks_len,
}
}
pub fn write(&self, out: &mut Vec<u8>, drep: Drep) {
drep.put_u32(out, self.args_max);
drep.put_u32(out, self.args_len);
drep.put_u32(out, self.max_count);
drep.put_u32(out, self.offset);
drep.put_u32(out, self.actual_count);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NdrResponse {
pub status: u32,
pub args_len: u32,
pub max_count: u32,
pub offset: u32,
pub actual_count: u32,
}
impl NdrResponse {
pub const LEN: usize = 20;
pub fn parse(buf: &[u8], drep: Drep) -> Result<(NdrResponse, &[u8]), RpcError> {
if buf.len() < Self::LEN {
return Err(RpcError::TooShort {
need: Self::LEN,
have: buf.len(),
});
}
let status = drep.u32(&buf[0..4]);
let args_len = drep.u32(&buf[4..8]);
let max_count = drep.u32(&buf[8..12]);
let offset = drep.u32(&buf[12..16]);
let actual_count = drep.u32(&buf[16..20]);
if args_len != actual_count {
return Err(RpcError::NdrMismatch("args_len != actual_count"));
}
if offset != 0 {
return Err(RpcError::NdrMismatch("array offset must be 0"));
}
let available = buf.len() - Self::LEN;
if actual_count as usize > available {
return Err(RpcError::NdrMismatch("actual_count overruns buffer"));
}
let n = NdrResponse {
status,
args_len,
max_count,
offset,
actual_count,
};
let blocks = &buf[Self::LEN..Self::LEN + actual_count as usize];
Ok((n, blocks))
}
pub fn ok(request_args_max: u32, blocks_len: u32) -> NdrResponse {
NdrResponse {
status: 0,
args_len: blocks_len,
max_count: request_args_max,
offset: 0,
actual_count: blocks_len,
}
}
pub fn error(status: u32, request_args_max: u32) -> NdrResponse {
NdrResponse {
status,
args_len: 0,
max_count: request_args_max,
offset: 0,
actual_count: 0,
}
}
pub fn write(&self, out: &mut Vec<u8>, drep: Drep) {
drep.put_u32(out, self.status);
drep.put_u32(out, self.args_len);
drep.put_u32(out, self.max_count);
drep.put_u32(out, self.offset);
drep.put_u32(out, self.actual_count);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rpc::{Drep, RpcHeader};
use crate::testutil::{golden, RPC_OFF};
const BODY: usize = RPC_OFF + RpcHeader::LEN;
#[test]
fn parse_connect_request_body_le() {
let f = golden("connect_req");
let (n, blocks) = NdrRequest::parse(&f[BODY..], Drep::LITTLE).unwrap();
assert_eq!(
(
n.args_max,
n.args_len,
n.max_count,
n.offset,
n.actual_count
),
(557, 557, 557, 0, 557)
);
assert_eq!(blocks.len(), 557);
assert_eq!(&blocks[..4], &[0x01, 0x01, 0x00, 0x5b]); }
#[test]
fn parse_connect_response_body_be() {
let f = golden("connect_res");
let (n, blocks) = NdrResponse::parse(&f[BODY..], Drep::BIG).unwrap();
assert_eq!(n.status, 0);
assert_eq!((n.args_len, n.max_count, n.actual_count), (90, 557, 90));
assert_eq!(blocks.len(), 90);
}
#[test]
fn response_ok_matches_golden_bytes() {
let f = golden("connect_res");
let mut out = Vec::new();
NdrResponse::ok(557, 90).write(&mut out, Drep::BIG);
assert_eq!(out, &f[BODY..BODY + 20]);
}
#[test]
fn request_for_blocks_matches_appready_golden() {
let f = golden("appready_req");
let mut out = Vec::new();
NdrRequest::for_blocks(1340, 32).write(&mut out, Drep::BIG);
assert_eq!(out, &f[BODY..BODY + 20]);
}
#[test]
fn mismatch_is_rejected() {
let mut f = golden("connect_req")[BODY..].to_vec();
f[16] = 0xff; assert!(matches!(
NdrRequest::parse(&f, Drep::LITTLE),
Err(RpcError::NdrMismatch(_))
));
assert!(matches!(
NdrRequest::parse(&f[..10], Drep::LITTLE),
Err(RpcError::TooShort { .. })
));
}
}