use std::borrow::Cow;
use std::time::{Duration, SystemTime};
use windows::Win32::Foundation::HANDLE;
use crate::error::InvalidParameterError;
use crate::security::SecurityDescriptor;
use crate::utils::OwnedHandle;
use crate::{Error, Result};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PipeName(String);
impl PipeName {
pub const PREFIX: &'static str = r"\\.\pipe\";
pub fn new(path: impl Into<String>) -> Result<Self> {
let path = path.into();
if path.is_empty() {
return Err(Error::InvalidParameter(InvalidParameterError::new(
"path",
"Pipe name cannot be empty",
)));
}
if !path.starts_with(Self::PREFIX) {
return Err(Error::InvalidParameter(InvalidParameterError::new(
"path",
"Pipe name must start with \\\\.\\pipe\\",
)));
}
if path == Self::PREFIX {
return Err(Error::InvalidParameter(InvalidParameterError::new(
"path",
"Pipe name must include a segment after \\\\.\\pipe\\",
)));
}
Ok(Self(path))
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn from_relative_name(name: impl AsRef<str>) -> Result<Self> {
let name = name.as_ref();
Self::new(format!("{}{}", Self::PREFIX, name))
}
}
impl std::fmt::Display for PipeName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone)]
pub struct NamedPipeInfo {
pub pipe_name: PipeName,
pub relative_name: String,
pub creation_time: Option<SystemTime>,
pub last_access_time: Option<SystemTime>,
pub last_write_time: Option<SystemTime>,
pub change_time: Option<SystemTime>,
pub end_of_file: i64,
pub allocation_size: i64,
pub file_attributes: u32,
pub file_index: u32,
pub local_info: Option<NamedPipeLocalInfo>,
}
impl NamedPipeInfo {
pub fn pipe_name(&self) -> &PipeName {
&self.pipe_name
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NamedPipeLocalInfo {
pub named_pipe_type: u32,
pub named_pipe_configuration: u32,
pub maximum_instances: u32,
pub current_instances: u32,
pub inbound_quota: u32,
pub read_data_available: u32,
pub outbound_quota: u32,
pub write_quota_available: u32,
pub named_pipe_state: u32,
pub named_pipe_end: u32,
}
#[derive(Debug, Clone)]
pub enum NamedPipeChange {
Appeared(NamedPipeInfo),
Removed(NamedPipeInfo),
}
pub(crate) fn filetime_to_system_time(filetime: i64) -> Option<SystemTime> {
const FILETIME_TO_UNIX_EPOCH: i64 = 116_444_736_000_000_000;
if filetime <= 0 {
return None;
}
let intervals_since_unix = filetime.saturating_sub(FILETIME_TO_UNIX_EPOCH);
let seconds = intervals_since_unix.div_euclid(10_000_000) as u64;
let nanos = (intervals_since_unix.rem_euclid(10_000_000) as u32) * 100;
Some(SystemTime::UNIX_EPOCH + Duration::new(seconds, nanos))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NamedPipeOpenMode {
Inbound,
Outbound,
Duplex,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NamedPipeType {
Byte,
Message,
}
#[derive(Debug, Clone)]
pub struct PipeSecurityOptions {
pub inherit_handle: bool,
pub security_descriptor: Option<SecurityDescriptor>,
}
impl PipeSecurityOptions {
pub fn new() -> Self {
Self {
inherit_handle: false,
security_descriptor: None,
}
}
pub fn inherit_handle(mut self, inherit_handle: bool) -> Self {
self.inherit_handle = inherit_handle;
self
}
pub fn security_descriptor(mut self, security_descriptor: SecurityDescriptor) -> Self {
self.security_descriptor = Some(security_descriptor);
self
}
}
impl Default for PipeSecurityOptions {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct PipeServerEndpoint {
handle: OwnedHandle,
pipe_name: PipeName,
open_mode: NamedPipeOpenMode,
pipe_type: NamedPipeType,
}
impl PipeServerEndpoint {
pub(crate) fn from_raw(
handle: HANDLE,
close_on_drop: bool,
pipe_name: PipeName,
open_mode: NamedPipeOpenMode,
pipe_type: NamedPipeType,
) -> Self {
Self {
handle: OwnedHandle::with_ownership(handle, close_on_drop),
pipe_name,
open_mode,
pipe_type,
}
}
pub fn raw_handle(&self) -> HANDLE {
self.handle.raw()
}
pub fn pipe_name(&self) -> &PipeName {
&self.pipe_name
}
pub fn open_mode(&self) -> NamedPipeOpenMode {
self.open_mode
}
pub fn pipe_type(&self) -> NamedPipeType {
self.pipe_type
}
pub fn set_close_on_drop(&mut self, close_on_drop: bool) {
self.handle.set_close_on_drop(close_on_drop);
}
}
#[derive(Debug)]
pub struct PipeClientEndpoint {
handle: OwnedHandle,
pipe_name: PipeName,
open_mode: NamedPipeOpenMode,
}
impl PipeClientEndpoint {
pub(crate) fn from_raw(
handle: HANDLE,
close_on_drop: bool,
pipe_name: PipeName,
open_mode: NamedPipeOpenMode,
) -> Self {
Self {
handle: OwnedHandle::with_ownership(handle, close_on_drop),
pipe_name,
open_mode,
}
}
pub fn raw_handle(&self) -> HANDLE {
self.handle.raw()
}
pub fn pipe_name(&self) -> &PipeName {
&self.pipe_name
}
pub fn open_mode(&self) -> NamedPipeOpenMode {
self.open_mode
}
pub fn set_close_on_drop(&mut self, close_on_drop: bool) {
self.handle.set_close_on_drop(close_on_drop);
}
}
pub(crate) fn to_cow_pipe_name(pipe_name: Option<&PipeName>) -> Cow<'static, str> {
match pipe_name {
Some(name) => Cow::Owned(name.to_string()),
None => Cow::Borrowed("<unnamed pipe>"),
}
}