Skip to main content

windows_erg/pipes/
types.rs

1use std::borrow::Cow;
2use std::time::{Duration, SystemTime};
3
4use windows::Win32::Foundation::HANDLE;
5
6use crate::error::InvalidParameterError;
7use crate::security::SecurityDescriptor;
8use crate::utils::OwnedHandle;
9use crate::{Error, Result};
10
11/// Canonical named pipe path.
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub struct PipeName(String);
14
15impl PipeName {
16    /// Prefix required by Win32 named pipes.
17    pub const PREFIX: &'static str = r"\\.\pipe\";
18
19    /// Validate and create a named pipe path.
20    pub fn new(path: impl Into<String>) -> Result<Self> {
21        let path = path.into();
22        if path.is_empty() {
23            return Err(Error::InvalidParameter(InvalidParameterError::new(
24                "path",
25                "Pipe name cannot be empty",
26            )));
27        }
28        if !path.starts_with(Self::PREFIX) {
29            return Err(Error::InvalidParameter(InvalidParameterError::new(
30                "path",
31                "Pipe name must start with \\\\.\\pipe\\",
32            )));
33        }
34        if path == Self::PREFIX {
35            return Err(Error::InvalidParameter(InvalidParameterError::new(
36                "path",
37                "Pipe name must include a segment after \\\\.\\pipe\\",
38            )));
39        }
40
41        Ok(Self(path))
42    }
43
44    /// Return the canonical named pipe path.
45    pub fn as_str(&self) -> &str {
46        &self.0
47    }
48
49    /// Create a canonical pipe name from a relative NamedPipe directory entry.
50    pub fn from_relative_name(name: impl AsRef<str>) -> Result<Self> {
51        let name = name.as_ref();
52        Self::new(format!("{}{}", Self::PREFIX, name))
53    }
54}
55
56impl std::fmt::Display for PipeName {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        write!(f, "{}", self.0)
59    }
60}
61
62/// Snapshot metadata for a named pipe currently present in the local pipe namespace.
63#[derive(Debug, Clone)]
64pub struct NamedPipeInfo {
65    /// Canonical pipe path (for example `\\.\pipe\my-pipe`).
66    pub pipe_name: PipeName,
67    /// Relative entry name as returned by the NamedPipe filesystem.
68    pub relative_name: String,
69    /// Optional creation time when the filesystem reports it.
70    pub creation_time: Option<SystemTime>,
71    /// Optional last access time when the filesystem reports it.
72    pub last_access_time: Option<SystemTime>,
73    /// Optional last write time when the filesystem reports it.
74    pub last_write_time: Option<SystemTime>,
75    /// Optional metadata change time when the filesystem reports it.
76    pub change_time: Option<SystemTime>,
77    /// End-of-file size reported by the filesystem.
78    pub end_of_file: i64,
79    /// Allocation size reported by the filesystem.
80    pub allocation_size: i64,
81    /// Raw Win32 file attribute bits.
82    pub file_attributes: u32,
83    /// Directory file index when reported by the filesystem.
84    pub file_index: u32,
85    /// Optional local pipe state details from `FilePipeLocalInformation`.
86    pub local_info: Option<NamedPipeLocalInfo>,
87}
88
89impl NamedPipeInfo {
90    /// Return the canonical pipe path.
91    pub fn pipe_name(&self) -> &PipeName {
92        &self.pipe_name
93    }
94}
95
96/// Local named-pipe state details returned by `FilePipeLocalInformation`.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub struct NamedPipeLocalInfo {
99    /// Named pipe type as reported by the kernel.
100    pub named_pipe_type: u32,
101    /// Server/client configuration value.
102    pub named_pipe_configuration: u32,
103    /// Maximum pipe instances allowed.
104    pub maximum_instances: u32,
105    /// Current number of connected/open instances.
106    pub current_instances: u32,
107    /// Inbound quota size in bytes.
108    pub inbound_quota: u32,
109    /// Bytes currently available for reading.
110    pub read_data_available: u32,
111    /// Outbound quota size in bytes.
112    pub outbound_quota: u32,
113    /// Remaining write quota in bytes.
114    pub write_quota_available: u32,
115    /// Current pipe state value.
116    pub named_pipe_state: u32,
117    /// Whether this handle points at server or client end.
118    pub named_pipe_end: u32,
119    /// PID of the server process that created this pipe, if available.
120    pub server_process_id: Option<crate::types::ProcessId>,
121}
122
123/// Change detected between named pipe snapshots.
124#[derive(Debug, Clone)]
125pub enum NamedPipeChange {
126    /// A pipe is present in the current snapshot but was absent previously.
127    Appeared(NamedPipeInfo),
128    /// A pipe disappeared since the previous snapshot.
129    Removed(NamedPipeInfo),
130}
131
132pub(crate) fn filetime_to_system_time(filetime: i64) -> Option<SystemTime> {
133    const FILETIME_TO_UNIX_EPOCH: i64 = 116_444_736_000_000_000;
134
135    if filetime <= 0 {
136        return None;
137    }
138
139    let intervals_since_unix = filetime.saturating_sub(FILETIME_TO_UNIX_EPOCH);
140    let seconds = intervals_since_unix.div_euclid(10_000_000) as u64;
141    let nanos = (intervals_since_unix.rem_euclid(10_000_000) as u32) * 100;
142
143    Some(SystemTime::UNIX_EPOCH + Duration::new(seconds, nanos))
144}
145
146/// Access direction for a named pipe instance.
147#[derive(Debug, Clone, Copy, PartialEq, Eq)]
148pub enum NamedPipeOpenMode {
149    /// Read-only server endpoint.
150    Inbound,
151    /// Write-only server endpoint.
152    Outbound,
153    /// Read/write server endpoint.
154    Duplex,
155}
156
157/// Message semantics for a named pipe.
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub enum NamedPipeType {
160    /// Byte stream mode.
161    Byte,
162    /// Message-framed mode.
163    Message,
164}
165
166/// Security attributes used when creating or opening a pipe handle.
167#[derive(Debug, Clone)]
168pub struct PipeSecurityOptions {
169    /// Whether spawned child processes can inherit this handle.
170    pub inherit_handle: bool,
171    /// Optional descriptor model used for ACL/owner semantics.
172    pub security_descriptor: Option<SecurityDescriptor>,
173}
174
175impl PipeSecurityOptions {
176    /// Create default security options.
177    pub fn new() -> Self {
178        Self {
179            inherit_handle: false,
180            security_descriptor: None,
181        }
182    }
183
184    /// Enable or disable handle inheritance.
185    pub fn inherit_handle(mut self, inherit_handle: bool) -> Self {
186        self.inherit_handle = inherit_handle;
187        self
188    }
189
190    /// Set a structured security descriptor model.
191    pub fn security_descriptor(mut self, security_descriptor: SecurityDescriptor) -> Self {
192        self.security_descriptor = Some(security_descriptor);
193        self
194    }
195}
196
197impl Default for PipeSecurityOptions {
198    fn default() -> Self {
199        Self::new()
200    }
201}
202
203/// Server-side named pipe endpoint handle.
204#[derive(Debug)]
205pub struct PipeServerEndpoint {
206    handle: OwnedHandle,
207    pipe_name: PipeName,
208    open_mode: NamedPipeOpenMode,
209    pipe_type: NamedPipeType,
210}
211
212impl PipeServerEndpoint {
213    /// Create a server endpoint from a raw handle.
214    pub(crate) fn from_raw(
215        handle: HANDLE,
216        close_on_drop: bool,
217        pipe_name: PipeName,
218        open_mode: NamedPipeOpenMode,
219        pipe_type: NamedPipeType,
220    ) -> Self {
221        Self {
222            handle: OwnedHandle::with_ownership(handle, close_on_drop),
223            pipe_name,
224            open_mode,
225            pipe_type,
226        }
227    }
228
229    /// Return underlying Win32 handle.
230    pub fn raw_handle(&self) -> HANDLE {
231        self.handle.raw()
232    }
233
234    /// Return named pipe path.
235    pub fn pipe_name(&self) -> &PipeName {
236        &self.pipe_name
237    }
238
239    /// Return open direction.
240    pub fn open_mode(&self) -> NamedPipeOpenMode {
241        self.open_mode
242    }
243
244    /// Return byte/message behavior.
245    pub fn pipe_type(&self) -> NamedPipeType {
246        self.pipe_type
247    }
248
249    /// Configure whether this handle should be closed on drop.
250    pub fn set_close_on_drop(&mut self, close_on_drop: bool) {
251        self.handle.set_close_on_drop(close_on_drop);
252    }
253}
254
255/// Client-side named pipe endpoint handle.
256#[derive(Debug)]
257pub struct PipeClientEndpoint {
258    handle: OwnedHandle,
259    pipe_name: PipeName,
260    open_mode: NamedPipeOpenMode,
261}
262
263impl PipeClientEndpoint {
264    /// Create a client endpoint from a raw handle.
265    pub(crate) fn from_raw(
266        handle: HANDLE,
267        close_on_drop: bool,
268        pipe_name: PipeName,
269        open_mode: NamedPipeOpenMode,
270    ) -> Self {
271        Self {
272            handle: OwnedHandle::with_ownership(handle, close_on_drop),
273            pipe_name,
274            open_mode,
275        }
276    }
277
278    /// Return underlying Win32 handle.
279    pub fn raw_handle(&self) -> HANDLE {
280        self.handle.raw()
281    }
282
283    /// Return named pipe path.
284    pub fn pipe_name(&self) -> &PipeName {
285        &self.pipe_name
286    }
287
288    /// Return open direction.
289    pub fn open_mode(&self) -> NamedPipeOpenMode {
290        self.open_mode
291    }
292
293    /// Configure whether this handle should be closed on drop.
294    pub fn set_close_on_drop(&mut self, close_on_drop: bool) {
295        self.handle.set_close_on_drop(close_on_drop);
296    }
297}
298
299pub(crate) fn to_cow_pipe_name(pipe_name: Option<&PipeName>) -> Cow<'static, str> {
300    match pipe_name {
301        Some(name) => Cow::Owned(name.to_string()),
302        None => Cow::Borrowed("<unnamed pipe>"),
303    }
304}