use std::net::IpAddr;
use anyhow::{Context, Result, ensure};
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::FromPrimitive;
use nix::libc::c_int;
pub const VERSION: i32 = 2;
#[derive(Clone, Copy, Debug, FromPrimitive, ToPrimitive)]
#[allow(clippy::upper_case_acronyms)]
pub enum RequestType {
GETPWBYNAME,
GETPWBYUID,
GETGRBYNAME,
GETGRBYGID,
GETHOSTBYNAME,
GETHOSTBYNAMEv6,
GETHOSTBYADDR,
GETHOSTBYADDRv6,
SHUTDOWN,
GETSTAT,
INVALIDATE,
GETFDPW,
GETFDGR,
GETFDHST,
GETAI,
INITGROUPS,
GETSERVBYNAME,
GETSERVBYPORT,
GETFDSERV,
GETNETGRENT,
INNETGR,
GETFDNETGR,
LASTREQ,
}
#[derive(Debug)]
pub struct Request<'a> {
pub ty: RequestType,
#[allow(dead_code)] pub key: &'a [u8],
}
impl<'a> Request<'a> {
pub fn parse(buf: &'a [u8]) -> Result<Self> {
ensure!(buf.len() >= 12, "request body too small: {}", buf.len());
let version = buf[0..4].try_into().map(i32::from_ne_bytes)?;
ensure!(version == VERSION, "wrong protocol version {version}");
let type_val = buf[4..8].try_into().map(i32::from_ne_bytes)?;
let ty = FromPrimitive::from_i32(type_val)
.with_context(|| format!("invalid enum value {type_val}"))?;
let key_len = buf[8..12].try_into().map(i32::from_ne_bytes)?;
let key_end = (12 + key_len).try_into()?;
ensure!(buf.len() >= key_end, "request body too small");
Ok(Request {
ty,
key: &buf[12..key_end],
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AiResponse {
pub addrs: Vec<IpAddr>,
pub canon_name: String,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct AiResponseHeader {
pub version: c_int,
pub found: c_int,
pub naddrs: c_int,
pub addrslen: c_int,
pub canonlen: c_int,
pub error: c_int,
}
impl AiResponseHeader {
pub fn parse(buf: &[u8]) -> Result<Self> {
ensure!(buf.len() >= 24, "request body too small: {}", buf.len());
let version = buf[0..4].try_into().map(i32::from_ne_bytes)?;
ensure!(version == VERSION, "wrong protocol version {version}");
let found = buf[4..8].try_into().map(i32::from_ne_bytes)?;
let naddrs = buf[8..12].try_into().map(i32::from_ne_bytes)?;
let addrslen = buf[12..16].try_into().map(i32::from_ne_bytes)?;
let canonlen = buf[16..20].try_into().map(i32::from_ne_bytes)?;
let error = buf[20..24].try_into().map(i32::from_ne_bytes)?;
Ok(AiResponseHeader {
version,
found,
naddrs,
addrslen,
canonlen,
error,
})
}
}