smb-rpc 0.11.1

MS-RPC implementations for `smb-rs`
Documentation
#![allow(unused_parens)]

use crate::{interface::*, pdu::DceRpcSyntaxId};
use smb_dtyp::make_guid;

use crate::ndr64::*;
use binrw::prelude::*;
use maybe_async::maybe_async;
use modular_bitfield::prelude::*;
/// [SHARE_ENUM_STRUCT](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-srvs/79ee052e-e16b-4ec5-b4b7-e99777c26eca>)
#[binrw::binrw]
#[derive(Debug, PartialEq, Eq)]
struct ShareEnumStruct {
    #[bw(calc = share_info.level().into())]
    level: NdrAlign<ShareInfoLevel>,
    #[br(args(*level))]
    share_info: NdrAlign<ShareEnumUnion>,
}

#[binrw::binrw]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[brw(repr(u32))]
pub enum ShareInfoLevel {
    Info0 = 0,
    Info1 = 1,
    Info2 = 2,
    Info501 = 501,
    Info502 = 502,
    Info503 = 503,
}

/// [`SHARE_ENUM_UNION`](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-srvs/7894d7e4-bb82-419c-b431-0247c8ae4dfe>)
#[binrw::binrw]
#[derive(Debug, PartialEq, Eq)]
#[br(import(level: ShareInfoLevel))]
enum ShareEnumUnion {
    #[brw(magic = 0u64)]
    #[br(pre_assert(level == ShareInfoLevel::Info0))]
    Info0(NdrPtr<ShareInfoContainer<ShareInfo0>>),
    #[brw(magic = 1u64)]
    #[br(pre_assert(level == ShareInfoLevel::Info1))]
    Info1(NdrPtr<ShareInfoContainer<ShareInfo1>>),
}

impl ShareEnumUnion {
    /// Returns the level of the share info contained in this union.
    pub fn level(&self) -> ShareInfoLevel {
        match self {
            ShareEnumUnion::Info0(_) => ShareInfoLevel::Info0,
            ShareEnumUnion::Info1(_) => ShareInfoLevel::Info1,
        }
    }
}

/// [`SHARE_INFO_1_CONTAINER`](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-srvs/919abd5d-87d9-4ffa-b4b1-632a66053bc6>)
#[binrw::binrw]
#[derive(Debug, PartialEq, Eq)]
struct ShareInfoContainer<T>
where
    T: ShareInfo,
{
    #[bw(calc = (buffer.as_ref().map_or(0, |x| x.len() as u32)).into())]
    entries_read: NdrAlign<u32>,
    #[br(args(None, NdrPtrReadMode::NoArraySupport, (*entries_read as u64,)))]
    buffer: NdrPtr<NdrArray<T>>,
}

trait ShareInfo:
    for<'a> BinRead<Args<'a> = (Option<&'a Self>,)>
    + for<'a> BinWrite<Args<'a> = (NdrPtrWriteStage,)>
    + Clone
    + PartialEq
    + Eq
    + 'static
{
}

/// [`SHARE_INFO_1`](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-srvs/fc69f110-998d-4c16-9667-514e22fdd80b>)
#[binrw::binrw]
#[derive(Debug, PartialEq, Eq, Clone)]
#[bw(import(stage: NdrPtrWriteStage))]
#[br(import(prev: Option<&Self>))]
pub struct ShareInfo1 {
    #[bw(args_raw(NdrPtrWriteArgs(stage, ())))]
    #[br(args(prev.map(|x| &x.netname), NdrPtrReadMode::WithArraySupport, ()))]
    pub netname: NdrPtr<NdrString<u16>>,
    #[bw(if(stage == NdrPtrWriteStage::ArraySupportWriteRefId))]
    #[br(args(prev.map(|x| &**x.share_type)))]
    pub share_type: NdrArrayStructureElement<ShareType>,
    #[bw(args_raw(NdrPtrWriteArgs(stage, ())))]
    #[br(args(prev.map(|x| &x.remark), NdrPtrReadMode::WithArraySupport, ()))]
    pub remark: NdrPtr<NdrString<u16>>,
}

/// [`SHARE_INFO_0`](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-srvs/73a25288-8086-4975-91a3-5cbee5b590cc>)
#[binrw::binrw]
#[derive(Debug, PartialEq, Eq, Clone)]
#[bw(import(stage: NdrPtrWriteStage))]
#[br(import(prev: Option<&Self>))]
pub struct ShareInfo0 {
    #[bw(args_raw(NdrPtrWriteArgs(stage, ())))]
    #[br(args(prev.map(|x| &x.netname), NdrPtrReadMode::WithArraySupport, ()))]
    netname: NdrPtr<NdrString<u16>>,
}

impl ShareInfo for ShareInfo0 {}
impl ShareInfo for ShareInfo1 {}

#[derive(BitfieldSpecifier, Debug, Clone, Copy, PartialEq, Eq)]
#[bits = 2]
pub enum ShareKind {
    Disk = 0,
    PrintQ = 1,
    Device = 2,
    IPC = 3,
}

/// Share types
///
/// [MS-SRVS](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-srvs/6069f8c0-c93f-43a0-a5b4-7ed447eb4b84>)
#[smb_dtyp::mbitfield]
pub struct ShareType {
    pub kind: ShareKind,
    #[skip]
    __: B23,
    pub cluster_fs: bool,
    pub cluster_sofs: bool,
    pub cluster_dfs: bool,
    #[skip]
    __: B2,
    pub temporary: bool,
    pub special: bool,
}

impl ShareType {
    /// Returns whether this is the windows IPC share (IPC$)
    pub fn is_win_ipc(&self) -> bool {
        self.kind() == ShareKind::IPC && self.special()
    }
}

// FYI: RPC top-level stub data is aligned to min(8, arg_size0, arg_size1, ...) bytes.
// DCE/RPC Chap. 12.3: RPC PDU Encodings/Alignment.

/// Input arguments for [NetrShareEnum](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-srvs/c4a98e7b-d416-439c-97bd-4d9f52f8ba52>)
#[binrw::binrw]
#[derive(Debug, PartialEq, Eq)]
struct NetrShareEnumIn {
    server_name: NdrAlign<NdrPtr<NdrString<u16>>, 4>,
    info_struct: NdrAlign<ShareEnumStruct, 4>,
    prefered_maximum_length: NdrAlign<u32, 4>,
    resume_handle: NdrAlign<NdrPtr<u32>, 4>,
}

/// Return value and out params of [NetrShareEnum](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-srvs/c4a98e7b-d416-439c-97bd-4d9f52f8ba52>)
#[binrw::binrw]
#[derive(Debug, PartialEq, Eq)]
struct NetrShareEnumOut {
    info_struct: NdrAlign<ShareEnumStruct, 4>,
    total_entries: NdrAlign<u32, 4>,
    resume_handle: NdrAlign<NdrPtr<u32>, 4>,
}

impl RpcCall for NetrShareEnumIn {
    const OPNUM: u16 = 0xf;

    type ResponseType = NetrShareEnumOut;
}

pub struct SrvSvc<T>
where
    T: BoundRpcConnection,
{
    bound_pipe: T,
}

impl<T> SrvSvc<T>
where
    T: BoundRpcConnection,
{
    #[maybe_async]
    pub async fn netr_share_enum(&mut self, server_name: &str) -> crate::Result<Vec<ShareInfo1>> {
        let input_struct = NetrShareEnumIn {
            server_name: NdrPtr::from(server_name.parse::<NdrString<u16>>().unwrap()).into(),
            info_struct: ShareEnumStruct {
                share_info: ShareEnumUnion::Info1(NdrPtr::from(ShareInfoContainer::<ShareInfo1> {
                    buffer: NdrPtr::from(None),
                }))
                .into(),
            }
            .into(),
            prefered_maximum_length: u32::MAX.into(),
            resume_handle: NdrPtr::<u32>::from(None).into(),
        };
        let enum_result = self.bound_pipe.send_receive(input_struct).await?;
        let mut result: Vec<ShareInfo1> = vec![];
        if let ShareEnumUnion::Info1(container) = &*enum_result.info_struct.share_info {
            match &**container {
                None => {
                    return Err(crate::SmbRpcError::InvalidResponseData(
                        "NetrShareEnum returned no data",
                    ));
                }
                Some(x) => match &*x.buffer {
                    None => {
                        return Err(crate::SmbRpcError::InvalidResponseData(
                            "NetrShareEnum returned no data",
                        ));
                    }
                    Some(y) => {
                        for share_info in y.iter() {
                            let share_info = &**share_info;
                            result.push(share_info.clone());
                        }
                    }
                },
            }
        }
        Ok(result)
    }
}

impl<T> super::base::RpcInterface<T> for SrvSvc<T>
where
    T: BoundRpcConnection,
{
    const SYNTAX_ID: DceRpcSyntaxId = DceRpcSyntaxId {
        uuid: make_guid!("4b324fc8-1670-01d3-1278-5a47bf6ee188"),
        version: 3,
    };

    fn new(bound_pipe: T) -> Self {
        SrvSvc { bound_pipe }
    }
}

#[cfg(test)]
mod test {
    use smb_tests::*;

    use super::*;

    test_binrw_read! {
        struct NetrShareEnumOut {
                info_struct: ShareEnumStruct {
                    share_info: ShareEnumUnion::Info1(
                        ShareInfoContainer::<ShareInfo1> {
                            buffer: Into::<NdrArray<ShareInfo1>>::into(vec![
                                ShareInfo1 {
                                    netname: "ADMIN$".parse::<NdrString<u16>>().unwrap().into(),
                                    share_type: ShareType::new().with_special(true).into(),
                                    remark: "Remote Admin"
                                        .parse::<NdrString<u16>>()
                                        .unwrap()
                                        .into(),
                                },
                                ShareInfo1 {
                                    netname: "C$".parse::<NdrString<u16>>().unwrap().into(),
                                    share_type: ShareType::new().with_special(true).into(),
                                    remark: "Default share"
                                        .parse::<NdrString<u16>>()
                                        .unwrap()
                                        .into(),
                                },
                                ShareInfo1 {
                                    netname: "IPC$".parse::<NdrString<u16>>().unwrap().into(),
                                    share_type: ShareType::new()
                                        .with_special(true)
                                        .with_kind(ShareKind::IPC)
                                        .into(),
                                    remark: "Remote IPC".parse::<NdrString<u16>>().unwrap().into(),
                                },
                                ShareInfo1 {
                                    netname: "LocalAdminShare"
                                        .parse::<NdrString<u16>>()
                                        .unwrap()
                                        .into(),
                                    share_type: ShareType::new().into(),
                                    remark: "".parse::<NdrString<u16>>().unwrap().into(),
                                },
                                ShareInfo1 {
                                    netname: "MyShare".parse::<NdrString<u16>>().unwrap().into(),
                                    share_type: ShareType::new().into(),
                                    remark: "".parse::<NdrString<u16>>().unwrap().into(),
                                },
                                ShareInfo1 {
                                    netname: "PublicShare"
                                        .parse::<NdrString<u16>>()
                                        .unwrap()
                                        .into(),
                                    share_type: ShareType::new().into(),
                                    remark: "".parse::<NdrString<u16>>().unwrap().into(),
                                },
                            ])
                            .into()
                        }
                        .into()
                    )
                    .into()
                }
                .into(),
                total_entries: 6.into(),
                resume_handle: NdrPtr::<u32>::from(None).into(),
            } => "010000000000000001000000000000000000020000000000060000000000000000000200000000000600000000000000000002000000000000000080000000000000020000000000000002000000000000000080000000000000020000000000000002000000000003000080000000000000020000000000000002000000000000000000000000000000020000000000000002000000000000000000000000000000020000000000000002000000000000000000000000000000020000000000070000000000000000000000000000000700000000000000410044004d0049004e002400000000000d0000000000000000000000000000000d00000000000000520065006d006f00740065002000410064006d0069006e00000000000000000003000000000000000000000000000000030000000000000043002400000000000e0000000000000000000000000000000e00000000000000440065006600610075006c007400200073006800610072006500000000000000050000000000000000000000000000000500000000000000490050004300240000000000000000000b0000000000000000000000000000000b00000000000000520065006d006f00740065002000490050004300000000001000000000000000000000000000000010000000000000004c006f00630061006c00410064006d0069006e0053006800610072006500000001000000000000000000000000000000010000000000000000000000000000000800000000000000000000000000000008000000000000004d00790053006800610072006500000001000000000000000000000000000000010000000000000000000000000000000c0000000000000000000000000000000c000000000000005000750062006c00690063005300680061007200650000000100000000000000000000000000000001000000000000000000000006000000000000000000000000000000"
    }

    smb_tests::test_binrw_write! {
        struct NetrShareEnumIn {
            server_name: Into::<NdrPtr<_>>::into(r"\\localhost".parse::<NdrString<u16>>().unwrap())
                .into(),
            info_struct: ShareEnumStruct {
                share_info: ShareEnumUnion::Info1(NdrPtr::from(ShareInfoContainer::<ShareInfo1> {
                    buffer: NdrPtr::from(None),
                }))
                .into(),
            }
            .into(),
            prefered_maximum_length: 4294967295.into(),
            resume_handle: NdrPtr::<u32>::from(None).into(),
        } => "00000200000000000c0000000000000000000000000000000c000000000000005c005c006c006f00630061006c0068006f0073007400000001000000000000000100000000000000000002000000000000000000000000000000000000000000ffffffff000000000000000000000000"
    }
}