use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DceRpcInterfaceUuid(pub [u8; 16]);
impl DceRpcInterfaceUuid {
#[inline]
pub const fn from_bytes(bytes: [u8; 16]) -> Self {
Self(bytes)
}
#[inline]
pub const fn as_bytes(&self) -> &[u8; 16] {
&self.0
}
pub fn well_known_name(&self) -> Option<&'static str> {
match self.canonical_string().as_str() {
"367abb81-9844-35f1-ad32-98f038001003" => Some("svcctl"), "338cd001-2244-31f1-aaaa-900038001003" => Some("winreg"), "12345778-1234-abcd-ef00-0123456789ab" => Some("lsarpc"), "12345778-1234-abcd-ef00-0123456789ac" => Some("samr"), "12345678-1234-abcd-ef00-01234567cffb" => Some("netlogon"), "12345678-1234-abcd-ef00-0123456789ab" => Some("spoolss"), "1ff70682-0a51-30e8-076d-740be8cee98b" => Some("atsvc"), "82273fdc-e32a-18c3-3f78-827929dc23ea" => Some("eventlog"), "6bffd098-a112-3610-9833-46c3f87e345a" => Some("wkssvc"), "4b324fc8-1670-01d3-1278-5a47bf6ee188" => Some("srvsvc"), "e3514235-4b06-11d1-ab04-00c04fc2dcd2" => Some("drsuapi"), _ => None,
}
}
pub fn canonical_string(&self) -> String {
let b = &self.0;
format!(
"{:02x}{:02x}{:02x}{:02x}-\
{:02x}{:02x}-\
{:02x}{:02x}-\
{:02x}{:02x}-\
{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
b[3],
b[2],
b[1],
b[0],
b[5],
b[4],
b[7],
b[6],
b[8],
b[9],
b[10],
b[11],
b[12],
b[13],
b[14],
b[15],
)
}
}
impl fmt::Display for DceRpcInterfaceUuid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.canonical_string())
}
}
impl fmt::Debug for DceRpcInterfaceUuid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DceRpcInterfaceUuid({})", self.canonical_string())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum SmbDialect {
V1,
V2,
EncryptedTransform,
CompressedTransform,
}
impl SmbDialect {
pub fn as_str(&self) -> &'static str {
match self {
SmbDialect::V1 => "smb1",
SmbDialect::V2 => "smb2",
SmbDialect::EncryptedTransform => "smb3_encrypted",
SmbDialect::CompressedTransform => "smb3_compressed",
}
}
pub fn is_opaque(&self) -> bool {
matches!(
self,
SmbDialect::EncryptedTransform | SmbDialect::CompressedTransform
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum SmbCommand {
Negotiate,
SessionSetup,
Logoff,
TreeConnect,
TreeDisconnect,
Create,
Close,
Flush,
Read,
Write,
Lock,
Ioctl,
Cancel,
Echo,
QueryDirectory,
ChangeNotify,
QueryInfo,
SetInfo,
OplockBreak,
Other(u16),
OtherSmb1(u8),
NotApplicable,
}
impl SmbCommand {
pub fn as_str(&self) -> &'static str {
match self {
SmbCommand::Negotiate => "negotiate",
SmbCommand::SessionSetup => "session_setup",
SmbCommand::Logoff => "logoff",
SmbCommand::TreeConnect => "tree_connect",
SmbCommand::TreeDisconnect => "tree_disconnect",
SmbCommand::Create => "create",
SmbCommand::Close => "close",
SmbCommand::Flush => "flush",
SmbCommand::Read => "read",
SmbCommand::Write => "write",
SmbCommand::Lock => "lock",
SmbCommand::Ioctl => "ioctl",
SmbCommand::Cancel => "cancel",
SmbCommand::Echo => "echo",
SmbCommand::QueryDirectory => "query_directory",
SmbCommand::ChangeNotify => "change_notify",
SmbCommand::QueryInfo => "query_info",
SmbCommand::SetInfo => "set_info",
SmbCommand::OplockBreak => "oplock_break",
SmbCommand::Other(_) => "other",
SmbCommand::OtherSmb1(_) => "smb1",
SmbCommand::NotApplicable => "n/a",
}
}
pub(crate) fn from_smb2(code: u16) -> Self {
match code {
0x0000 => SmbCommand::Negotiate,
0x0001 => SmbCommand::SessionSetup,
0x0002 => SmbCommand::Logoff,
0x0003 => SmbCommand::TreeConnect,
0x0004 => SmbCommand::TreeDisconnect,
0x0005 => SmbCommand::Create,
0x0006 => SmbCommand::Close,
0x0007 => SmbCommand::Flush,
0x0008 => SmbCommand::Read,
0x0009 => SmbCommand::Write,
0x000A => SmbCommand::Lock,
0x000B => SmbCommand::Ioctl,
0x000C => SmbCommand::Cancel,
0x000D => SmbCommand::Echo,
0x000E => SmbCommand::QueryDirectory,
0x000F => SmbCommand::ChangeNotify,
0x0010 => SmbCommand::QueryInfo,
0x0011 => SmbCommand::SetInfo,
0x0012 => SmbCommand::OplockBreak,
other => SmbCommand::Other(other),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct SmbMessage {
pub dialect: SmbDialect,
pub command: SmbCommand,
pub message_id: Option<u64>,
pub tree_id: Option<u32>,
pub session_id: Option<u64>,
pub encrypted: bool,
pub tree_connect_path: Option<String>,
pub tree_connect_is_admin_share: bool,
pub create_path: Option<String>,
pub create_is_admin_named_pipe: bool,
pub read_length: Option<u32>,
pub read_offset: Option<u64>,
pub write_length: Option<u32>,
pub write_offset: Option<u64>,
pub ntlm_auth: Option<NtlmAuth>,
pub dcerpc_bind_uuids: Vec<DceRpcInterfaceUuid>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct NtlmAuth {
pub domain: Option<String>,
pub username: Option<String>,
pub workstation: Option<String>,
}
impl SmbMessage {
pub(crate) fn new(dialect: SmbDialect, command: SmbCommand) -> Self {
Self {
dialect,
command,
message_id: None,
tree_id: None,
session_id: None,
encrypted: matches!(dialect, SmbDialect::EncryptedTransform),
tree_connect_path: None,
tree_connect_is_admin_share: false,
create_path: None,
create_is_admin_named_pipe: false,
read_length: None,
read_offset: None,
write_length: None,
write_offset: None,
ntlm_auth: None,
dcerpc_bind_uuids: Vec::new(),
}
}
}