|
@@ -20,21 +20,18 @@ cfg_io_util! {
|
|
|
#[cfg(not(docsrs))]
|
|
|
mod doc {
|
|
|
pub(super) use crate::os::windows::ffi::OsStrExt;
|
|
|
- pub(super) use crate::winapi::shared::minwindef::{DWORD, FALSE};
|
|
|
- pub(super) use crate::winapi::um::fileapi;
|
|
|
- pub(super) use crate::winapi::um::handleapi;
|
|
|
- pub(super) use crate::winapi::um::namedpipeapi;
|
|
|
- pub(super) use crate::winapi::um::winbase;
|
|
|
- pub(super) use crate::winapi::um::winnt;
|
|
|
-
|
|
|
+ pub(super) mod windows_sys {
|
|
|
+ pub(crate) use windows_sys::{
|
|
|
+ Win32::Foundation::*, Win32::Storage::FileSystem::*, Win32::System::Pipes::*,
|
|
|
+ Win32::System::SystemServices::*,
|
|
|
+ };
|
|
|
+ }
|
|
|
pub(super) use mio::windows as mio_windows;
|
|
|
}
|
|
|
|
|
|
// NB: none of these shows up in public API, so don't document them.
|
|
|
#[cfg(docsrs)]
|
|
|
mod doc {
|
|
|
- pub type DWORD = crate::doc::NotDefinedHere;
|
|
|
-
|
|
|
pub(super) mod mio_windows {
|
|
|
pub type NamedPipe = crate::doc::NotDefinedHere;
|
|
|
}
|
|
@@ -101,7 +98,6 @@ use self::doc::*;
|
|
|
/// # Ok(()) }
|
|
|
/// ```
|
|
|
///
|
|
|
-/// [`ERROR_PIPE_BUSY`]: crate::winapi::shared::winerror::ERROR_PIPE_BUSY
|
|
|
/// [Windows named pipe]: https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes
|
|
|
#[derive(Debug)]
|
|
|
pub struct NamedPipeServer {
|
|
@@ -192,17 +188,15 @@ impl NamedPipeServer {
|
|
|
/// # Ok(()) }
|
|
|
/// ```
|
|
|
pub async fn connect(&self) -> io::Result<()> {
|
|
|
- loop {
|
|
|
- match self.io.connect() {
|
|
|
- Ok(()) => break,
|
|
|
- Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
|
|
- self.io.registration().readiness(Interest::WRITABLE).await?;
|
|
|
- }
|
|
|
- Err(e) => return Err(e),
|
|
|
+ match self.io.connect() {
|
|
|
+ Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
|
|
+ self.io
|
|
|
+ .registration()
|
|
|
+ .async_io(Interest::WRITABLE, || self.io.connect())
|
|
|
+ .await
|
|
|
}
|
|
|
+ x => x,
|
|
|
}
|
|
|
-
|
|
|
- Ok(())
|
|
|
}
|
|
|
|
|
|
/// Disconnects the server end of a named pipe instance from a client
|
|
@@ -211,7 +205,7 @@ impl NamedPipeServer {
|
|
|
/// ```
|
|
|
/// use tokio::io::AsyncWriteExt;
|
|
|
/// use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
|
|
|
- /// use winapi::shared::winerror;
|
|
|
+ /// use windows_sys::Win32::Foundation::ERROR_PIPE_NOT_CONNECTED;
|
|
|
///
|
|
|
/// const PIPE_NAME: &str = r"\\.\pipe\tokio-named-pipe-disconnect";
|
|
|
///
|
|
@@ -231,7 +225,7 @@ impl NamedPipeServer {
|
|
|
/// // Write fails with an OS-specific error after client has been
|
|
|
/// // disconnected.
|
|
|
/// let e = client.write(b"ping").await.unwrap_err();
|
|
|
- /// assert_eq!(e.raw_os_error(), Some(winerror::ERROR_PIPE_NOT_CONNECTED as i32));
|
|
|
+ /// assert_eq!(e.raw_os_error(), Some(ERROR_PIPE_NOT_CONNECTED as i32));
|
|
|
/// # Ok(()) }
|
|
|
/// ```
|
|
|
pub fn disconnect(&self) -> io::Result<()> {
|
|
@@ -244,6 +238,12 @@ impl NamedPipeServer {
|
|
|
/// can be used to concurrently read / write to the same pipe on a single
|
|
|
/// task without splitting the pipe.
|
|
|
///
|
|
|
+ /// The function may complete without the pipe being ready. This is a
|
|
|
+ /// false-positive and attempting an operation will return with
|
|
|
+ /// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
|
|
+ /// [`Ready`] set, so you should always check the returned value and possibly
|
|
|
+ /// wait again if the requested states are not set.
|
|
|
+ ///
|
|
|
/// # Examples
|
|
|
///
|
|
|
/// Concurrently read and write to the pipe on the same task without
|
|
@@ -403,8 +403,12 @@ impl NamedPipeServer {
|
|
|
/// # Return
|
|
|
///
|
|
|
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
|
|
- /// number of bytes read. `Ok(0)` indicates the pipe's read half is closed
|
|
|
- /// and will no longer yield data. If the pipe is not ready to read data
|
|
|
+ /// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
|
|
|
+ ///
|
|
|
+ /// 1. The pipe's read half is closed and will no longer yield data.
|
|
|
+ /// 2. The specified buffer was 0 bytes in length.
|
|
|
+ ///
|
|
|
+ /// If the pipe is not ready to read data,
|
|
|
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
|
|
///
|
|
|
/// # Examples
|
|
@@ -536,7 +540,7 @@ impl NamedPipeServer {
|
|
|
/// Tries to read data from the stream into the provided buffer, advancing the
|
|
|
/// buffer's internal cursor, returning how many bytes were read.
|
|
|
///
|
|
|
- /// Receives any pending data from the socket but does not wait for new data
|
|
|
+ /// Receives any pending data from the pipe but does not wait for new data
|
|
|
/// to arrive. On success, returns the number of bytes read. Because
|
|
|
/// `try_read_buf()` is non-blocking, the buffer does not have to be stored by
|
|
|
/// the async task and can exist entirely on the stack.
|
|
@@ -567,7 +571,7 @@ impl NamedPipeServer {
|
|
|
/// let server = named_pipe::ServerOptions::new().create(PIPE_NAME)?;
|
|
|
///
|
|
|
/// loop {
|
|
|
- /// // Wait for the socket to be readable
|
|
|
+ /// // Wait for the pipe to be readable
|
|
|
/// server.readable().await?;
|
|
|
///
|
|
|
/// let mut buf = Vec::with_capacity(4096);
|
|
@@ -808,27 +812,32 @@ impl NamedPipeServer {
|
|
|
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(buf))
|
|
|
}
|
|
|
|
|
|
- /// Tries to read or write from the socket using a user-provided IO operation.
|
|
|
+ /// Tries to read or write from the pipe using a user-provided IO operation.
|
|
|
///
|
|
|
- /// If the socket is ready, the provided closure is called. The closure
|
|
|
- /// should attempt to perform IO operation from the socket by manually
|
|
|
+ /// If the pipe is ready, the provided closure is called. The closure
|
|
|
+ /// should attempt to perform IO operation from the pipe by manually
|
|
|
/// calling the appropriate syscall. If the operation fails because the
|
|
|
- /// socket is not actually ready, then the closure should return a
|
|
|
+ /// pipe is not actually ready, then the closure should return a
|
|
|
/// `WouldBlock` error and the readiness flag is cleared. The return value
|
|
|
/// of the closure is then returned by `try_io`.
|
|
|
///
|
|
|
- /// If the socket is not ready, then the closure is not called
|
|
|
+ /// If the pipe is not ready, then the closure is not called
|
|
|
/// and a `WouldBlock` error is returned.
|
|
|
///
|
|
|
/// The closure should only return a `WouldBlock` error if it has performed
|
|
|
- /// an IO operation on the socket that failed due to the socket not being
|
|
|
+ /// an IO operation on the pipe that failed due to the pipe not being
|
|
|
/// ready. Returning a `WouldBlock` error in any other situation will
|
|
|
- /// incorrectly clear the readiness flag, which can cause the socket to
|
|
|
+ /// incorrectly clear the readiness flag, which can cause the pipe to
|
|
|
/// behave incorrectly.
|
|
|
///
|
|
|
/// The closure should not perform the IO operation using any of the
|
|
|
/// methods defined on the Tokio `NamedPipeServer` type, as this will mess with
|
|
|
- /// the readiness flag and can cause the socket to behave incorrectly.
|
|
|
+ /// the readiness flag and can cause the pipe to behave incorrectly.
|
|
|
+ ///
|
|
|
+ /// This method is not intended to be used with combined interests.
|
|
|
+ /// The closure should perform only one type of IO operation, so it should not
|
|
|
+ /// require more than one ready state. This method may panic or sleep forever
|
|
|
+ /// if it is called with a combined interest.
|
|
|
///
|
|
|
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
|
|
|
///
|
|
@@ -903,7 +912,7 @@ impl AsRawHandle for NamedPipeServer {
|
|
|
/// use std::time::Duration;
|
|
|
/// use tokio::net::windows::named_pipe::ClientOptions;
|
|
|
/// use tokio::time;
|
|
|
-/// use winapi::shared::winerror;
|
|
|
+/// use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY;
|
|
|
///
|
|
|
/// const PIPE_NAME: &str = r"\\.\pipe\named-pipe-idiomatic-client";
|
|
|
///
|
|
@@ -911,7 +920,7 @@ impl AsRawHandle for NamedPipeServer {
|
|
|
/// let client = loop {
|
|
|
/// match ClientOptions::new().open(PIPE_NAME) {
|
|
|
/// Ok(client) => break client,
|
|
|
-/// Err(e) if e.raw_os_error() == Some(winerror::ERROR_PIPE_BUSY as i32) => (),
|
|
|
+/// Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (),
|
|
|
/// Err(e) => return Err(e),
|
|
|
/// }
|
|
|
///
|
|
@@ -922,7 +931,7 @@ impl AsRawHandle for NamedPipeServer {
|
|
|
/// # Ok(()) }
|
|
|
/// ```
|
|
|
///
|
|
|
-/// [`ERROR_PIPE_BUSY`]: crate::winapi::shared::winerror::ERROR_PIPE_BUSY
|
|
|
+/// [`ERROR_PIPE_BUSY`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/Foundation/constant.ERROR_PIPE_BUSY.html
|
|
|
/// [Windows named pipe]: https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes
|
|
|
#[derive(Debug)]
|
|
|
pub struct NamedPipeClient {
|
|
@@ -986,6 +995,12 @@ impl NamedPipeClient {
|
|
|
/// can be used to concurrently read / write to the same pipe on a single
|
|
|
/// task without splitting the pipe.
|
|
|
///
|
|
|
+ /// The function may complete without the pipe being ready. This is a
|
|
|
+ /// false-positive and attempting an operation will return with
|
|
|
+ /// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
|
|
+ /// [`Ready`] set, so you should always check the returned value and possibly
|
|
|
+ /// wait again if the requested states are not set.
|
|
|
+ ///
|
|
|
/// # Examples
|
|
|
///
|
|
|
/// Concurrently read and write to the pipe on the same task without
|
|
@@ -1143,8 +1158,12 @@ impl NamedPipeClient {
|
|
|
/// # Return
|
|
|
///
|
|
|
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
|
|
- /// number of bytes read. `Ok(0)` indicates the pipe's read half is closed
|
|
|
- /// and will no longer yield data. If the pipe is not ready to read data
|
|
|
+ /// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
|
|
|
+ ///
|
|
|
+ /// 1. The pipe's read half is closed and will no longer yield data.
|
|
|
+ /// 2. The specified buffer was 0 bytes in length.
|
|
|
+ ///
|
|
|
+ /// If the pipe is not ready to read data,
|
|
|
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
|
|
///
|
|
|
/// # Examples
|
|
@@ -1274,7 +1293,7 @@ impl NamedPipeClient {
|
|
|
/// Tries to read data from the stream into the provided buffer, advancing the
|
|
|
/// buffer's internal cursor, returning how many bytes were read.
|
|
|
///
|
|
|
- /// Receives any pending data from the socket but does not wait for new data
|
|
|
+ /// Receives any pending data from the pipe but does not wait for new data
|
|
|
/// to arrive. On success, returns the number of bytes read. Because
|
|
|
/// `try_read_buf()` is non-blocking, the buffer does not have to be stored by
|
|
|
/// the async task and can exist entirely on the stack.
|
|
@@ -1305,7 +1324,7 @@ impl NamedPipeClient {
|
|
|
/// let client = named_pipe::ClientOptions::new().open(PIPE_NAME)?;
|
|
|
///
|
|
|
/// loop {
|
|
|
- /// // Wait for the socket to be readable
|
|
|
+ /// // Wait for the pipe to be readable
|
|
|
/// client.readable().await?;
|
|
|
///
|
|
|
/// let mut buf = Vec::with_capacity(4096);
|
|
@@ -1543,27 +1562,32 @@ impl NamedPipeClient {
|
|
|
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(buf))
|
|
|
}
|
|
|
|
|
|
- /// Tries to read or write from the socket using a user-provided IO operation.
|
|
|
+ /// Tries to read or write from the pipe using a user-provided IO operation.
|
|
|
///
|
|
|
- /// If the socket is ready, the provided closure is called. The closure
|
|
|
- /// should attempt to perform IO operation from the socket by manually
|
|
|
+ /// If the pipe is ready, the provided closure is called. The closure
|
|
|
+ /// should attempt to perform IO operation from the pipe by manually
|
|
|
/// calling the appropriate syscall. If the operation fails because the
|
|
|
- /// socket is not actually ready, then the closure should return a
|
|
|
+ /// pipe is not actually ready, then the closure should return a
|
|
|
/// `WouldBlock` error and the readiness flag is cleared. The return value
|
|
|
/// of the closure is then returned by `try_io`.
|
|
|
///
|
|
|
- /// If the socket is not ready, then the closure is not called
|
|
|
+ /// If the pipe is not ready, then the closure is not called
|
|
|
/// and a `WouldBlock` error is returned.
|
|
|
///
|
|
|
/// The closure should only return a `WouldBlock` error if it has performed
|
|
|
- /// an IO operation on the socket that failed due to the socket not being
|
|
|
+ /// an IO operation on the pipe that failed due to the pipe not being
|
|
|
/// ready. Returning a `WouldBlock` error in any other situation will
|
|
|
- /// incorrectly clear the readiness flag, which can cause the socket to
|
|
|
+ /// incorrectly clear the readiness flag, which can cause the pipe to
|
|
|
/// behave incorrectly.
|
|
|
///
|
|
|
/// The closure should not perform the IO operation using any of the methods
|
|
|
/// defined on the Tokio `NamedPipeClient` type, as this will mess with the
|
|
|
- /// readiness flag and can cause the socket to behave incorrectly.
|
|
|
+ /// readiness flag and can cause the pipe to behave incorrectly.
|
|
|
+ ///
|
|
|
+ /// This method is not intended to be used with combined interests.
|
|
|
+ /// The closure should perform only one type of IO operation, so it should not
|
|
|
+ /// require more than one ready state. This method may panic or sleep forever
|
|
|
+ /// if it is called with a combined interest.
|
|
|
///
|
|
|
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
|
|
|
///
|
|
@@ -1641,12 +1665,12 @@ macro_rules! bool_flag {
|
|
|
/// See [`ServerOptions::create`].
|
|
|
#[derive(Debug, Clone)]
|
|
|
pub struct ServerOptions {
|
|
|
- open_mode: DWORD,
|
|
|
- pipe_mode: DWORD,
|
|
|
- max_instances: DWORD,
|
|
|
- out_buffer_size: DWORD,
|
|
|
- in_buffer_size: DWORD,
|
|
|
- default_timeout: DWORD,
|
|
|
+ open_mode: u32,
|
|
|
+ pipe_mode: u32,
|
|
|
+ max_instances: u32,
|
|
|
+ out_buffer_size: u32,
|
|
|
+ in_buffer_size: u32,
|
|
|
+ default_timeout: u32,
|
|
|
}
|
|
|
|
|
|
impl ServerOptions {
|
|
@@ -1663,9 +1687,9 @@ impl ServerOptions {
|
|
|
/// ```
|
|
|
pub fn new() -> ServerOptions {
|
|
|
ServerOptions {
|
|
|
- open_mode: winbase::PIPE_ACCESS_DUPLEX | winbase::FILE_FLAG_OVERLAPPED,
|
|
|
- pipe_mode: winbase::PIPE_TYPE_BYTE | winbase::PIPE_REJECT_REMOTE_CLIENTS,
|
|
|
- max_instances: winbase::PIPE_UNLIMITED_INSTANCES,
|
|
|
+ open_mode: windows_sys::PIPE_ACCESS_DUPLEX | windows_sys::FILE_FLAG_OVERLAPPED,
|
|
|
+ pipe_mode: windows_sys::PIPE_TYPE_BYTE | windows_sys::PIPE_REJECT_REMOTE_CLIENTS,
|
|
|
+ max_instances: windows_sys::PIPE_UNLIMITED_INSTANCES,
|
|
|
out_buffer_size: 65536,
|
|
|
in_buffer_size: 65536,
|
|
|
default_timeout: 0,
|
|
@@ -1681,11 +1705,10 @@ impl ServerOptions {
|
|
|
///
|
|
|
/// [`dwPipeMode`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
|
|
|
pub fn pipe_mode(&mut self, pipe_mode: PipeMode) -> &mut Self {
|
|
|
- self.pipe_mode = match pipe_mode {
|
|
|
- PipeMode::Byte => winbase::PIPE_TYPE_BYTE,
|
|
|
- PipeMode::Message => winbase::PIPE_TYPE_MESSAGE,
|
|
|
- };
|
|
|
-
|
|
|
+ let is_msg = matches!(pipe_mode, PipeMode::Message);
|
|
|
+ // Pipe mode is implemented as a bit flag 0x4. Set is message and unset
|
|
|
+ // is byte.
|
|
|
+ bool_flag!(self.pipe_mode, is_msg, windows_sys::PIPE_TYPE_MESSAGE);
|
|
|
self
|
|
|
}
|
|
|
|
|
@@ -1781,7 +1804,7 @@ impl ServerOptions {
|
|
|
/// # Ok(()) }
|
|
|
/// ```
|
|
|
pub fn access_inbound(&mut self, allowed: bool) -> &mut Self {
|
|
|
- bool_flag!(self.open_mode, allowed, winbase::PIPE_ACCESS_INBOUND);
|
|
|
+ bool_flag!(self.open_mode, allowed, windows_sys::PIPE_ACCESS_INBOUND);
|
|
|
self
|
|
|
}
|
|
|
|
|
@@ -1879,7 +1902,7 @@ impl ServerOptions {
|
|
|
/// # Ok(()) }
|
|
|
/// ```
|
|
|
pub fn access_outbound(&mut self, allowed: bool) -> &mut Self {
|
|
|
- bool_flag!(self.open_mode, allowed, winbase::PIPE_ACCESS_OUTBOUND);
|
|
|
+ bool_flag!(self.open_mode, allowed, windows_sys::PIPE_ACCESS_OUTBOUND);
|
|
|
self
|
|
|
}
|
|
|
|
|
@@ -1950,7 +1973,113 @@ impl ServerOptions {
|
|
|
bool_flag!(
|
|
|
self.open_mode,
|
|
|
first,
|
|
|
- winbase::FILE_FLAG_FIRST_PIPE_INSTANCE
|
|
|
+ windows_sys::FILE_FLAG_FIRST_PIPE_INSTANCE
|
|
|
+ );
|
|
|
+ self
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Requests permission to modify the pipe's discretionary access control list.
|
|
|
+ ///
|
|
|
+ /// This corresponds to setting [`WRITE_DAC`] in dwOpenMode.
|
|
|
+ ///
|
|
|
+ /// # Examples
|
|
|
+ ///
|
|
|
+ /// ```
|
|
|
+ /// use std::{io, os::windows::prelude::AsRawHandle, ptr};
|
|
|
+ //
|
|
|
+ /// use tokio::net::windows::named_pipe::ServerOptions;
|
|
|
+ /// use windows_sys::{
|
|
|
+ /// Win32::Foundation::ERROR_SUCCESS,
|
|
|
+ /// Win32::Security::DACL_SECURITY_INFORMATION,
|
|
|
+ /// Win32::Security::Authorization::{SetSecurityInfo, SE_KERNEL_OBJECT},
|
|
|
+ /// };
|
|
|
+ ///
|
|
|
+ /// const PIPE_NAME: &str = r"\\.\pipe\write_dac_pipe";
|
|
|
+ ///
|
|
|
+ /// # #[tokio::main] async fn main() -> io::Result<()> {
|
|
|
+ /// let mut pipe_template = ServerOptions::new();
|
|
|
+ /// pipe_template.write_dac(true);
|
|
|
+ /// let pipe = pipe_template.create(PIPE_NAME)?;
|
|
|
+ ///
|
|
|
+ /// unsafe {
|
|
|
+ /// assert_eq!(
|
|
|
+ /// ERROR_SUCCESS,
|
|
|
+ /// SetSecurityInfo(
|
|
|
+ /// pipe.as_raw_handle() as _,
|
|
|
+ /// SE_KERNEL_OBJECT,
|
|
|
+ /// DACL_SECURITY_INFORMATION,
|
|
|
+ /// ptr::null_mut(),
|
|
|
+ /// ptr::null_mut(),
|
|
|
+ /// ptr::null_mut(),
|
|
|
+ /// ptr::null_mut(),
|
|
|
+ /// )
|
|
|
+ /// );
|
|
|
+ /// }
|
|
|
+ ///
|
|
|
+ /// # Ok(()) }
|
|
|
+ /// ```
|
|
|
+ ///
|
|
|
+ /// ```
|
|
|
+ /// use std::{io, os::windows::prelude::AsRawHandle, ptr};
|
|
|
+ //
|
|
|
+ /// use tokio::net::windows::named_pipe::ServerOptions;
|
|
|
+ /// use windows_sys::{
|
|
|
+ /// Win32::Foundation::ERROR_ACCESS_DENIED,
|
|
|
+ /// Win32::Security::DACL_SECURITY_INFORMATION,
|
|
|
+ /// Win32::Security::Authorization::{SetSecurityInfo, SE_KERNEL_OBJECT},
|
|
|
+ /// };
|
|
|
+ ///
|
|
|
+ /// const PIPE_NAME: &str = r"\\.\pipe\write_dac_pipe_fail";
|
|
|
+ ///
|
|
|
+ /// # #[tokio::main] async fn main() -> io::Result<()> {
|
|
|
+ /// let mut pipe_template = ServerOptions::new();
|
|
|
+ /// pipe_template.write_dac(false);
|
|
|
+ /// let pipe = pipe_template.create(PIPE_NAME)?;
|
|
|
+ ///
|
|
|
+ /// unsafe {
|
|
|
+ /// assert_eq!(
|
|
|
+ /// ERROR_ACCESS_DENIED,
|
|
|
+ /// SetSecurityInfo(
|
|
|
+ /// pipe.as_raw_handle() as _,
|
|
|
+ /// SE_KERNEL_OBJECT,
|
|
|
+ /// DACL_SECURITY_INFORMATION,
|
|
|
+ /// ptr::null_mut(),
|
|
|
+ /// ptr::null_mut(),
|
|
|
+ /// ptr::null_mut(),
|
|
|
+ /// ptr::null_mut(),
|
|
|
+ /// )
|
|
|
+ /// );
|
|
|
+ /// }
|
|
|
+ ///
|
|
|
+ /// # Ok(()) }
|
|
|
+ /// ```
|
|
|
+ ///
|
|
|
+ /// [`WRITE_DAC`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
|
|
|
+ pub fn write_dac(&mut self, requested: bool) -> &mut Self {
|
|
|
+ bool_flag!(self.open_mode, requested, windows_sys::WRITE_DAC);
|
|
|
+ self
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Requests permission to modify the pipe's owner.
|
|
|
+ ///
|
|
|
+ /// This corresponds to setting [`WRITE_OWNER`] in dwOpenMode.
|
|
|
+ ///
|
|
|
+ /// [`WRITE_OWNER`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
|
|
|
+ pub fn write_owner(&mut self, requested: bool) -> &mut Self {
|
|
|
+ bool_flag!(self.open_mode, requested, windows_sys::WRITE_OWNER);
|
|
|
+ self
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Requests permission to modify the pipe's system access control list.
|
|
|
+ ///
|
|
|
+ /// This corresponds to setting [`ACCESS_SYSTEM_SECURITY`] in dwOpenMode.
|
|
|
+ ///
|
|
|
+ /// [`ACCESS_SYSTEM_SECURITY`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
|
|
|
+ pub fn access_system_security(&mut self, requested: bool) -> &mut Self {
|
|
|
+ bool_flag!(
|
|
|
+ self.open_mode,
|
|
|
+ requested,
|
|
|
+ windows_sys::ACCESS_SYSTEM_SECURITY
|
|
|
);
|
|
|
self
|
|
|
}
|
|
@@ -1962,7 +2091,11 @@ impl ServerOptions {
|
|
|
///
|
|
|
/// [`PIPE_REJECT_REMOTE_CLIENTS`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea#pipe_reject_remote_clients
|
|
|
pub fn reject_remote_clients(&mut self, reject: bool) -> &mut Self {
|
|
|
- bool_flag!(self.pipe_mode, reject, winbase::PIPE_REJECT_REMOTE_CLIENTS);
|
|
|
+ bool_flag!(
|
|
|
+ self.pipe_mode,
|
|
|
+ reject,
|
|
|
+ windows_sys::PIPE_REJECT_REMOTE_CLIENTS
|
|
|
+ );
|
|
|
self
|
|
|
}
|
|
|
|
|
@@ -1984,7 +2117,7 @@ impl ServerOptions {
|
|
|
/// ```
|
|
|
/// use std::io;
|
|
|
/// use tokio::net::windows::named_pipe::{ServerOptions, ClientOptions};
|
|
|
- /// use winapi::shared::winerror;
|
|
|
+ /// use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY;
|
|
|
///
|
|
|
/// const PIPE_NAME: &str = r"\\.\pipe\tokio-named-pipe-max-instances";
|
|
|
///
|
|
@@ -2000,11 +2133,11 @@ impl ServerOptions {
|
|
|
///
|
|
|
/// // Too many servers!
|
|
|
/// let e = server.create(PIPE_NAME).unwrap_err();
|
|
|
- /// assert_eq!(e.raw_os_error(), Some(winerror::ERROR_PIPE_BUSY as i32));
|
|
|
+ /// assert_eq!(e.raw_os_error(), Some(ERROR_PIPE_BUSY as i32));
|
|
|
///
|
|
|
/// // Still too many servers even if we specify a higher value!
|
|
|
/// let e = server.max_instances(100).create(PIPE_NAME).unwrap_err();
|
|
|
- /// assert_eq!(e.raw_os_error(), Some(winerror::ERROR_PIPE_BUSY as i32));
|
|
|
+ /// assert_eq!(e.raw_os_error(), Some(ERROR_PIPE_BUSY as i32));
|
|
|
/// # Ok(()) }
|
|
|
/// ```
|
|
|
///
|
|
@@ -2020,9 +2153,10 @@ impl ServerOptions {
|
|
|
/// let builder = ServerOptions::new().max_instances(255);
|
|
|
/// # Ok(()) }
|
|
|
/// ```
|
|
|
+ #[track_caller]
|
|
|
pub fn max_instances(&mut self, instances: usize) -> &mut Self {
|
|
|
assert!(instances < 255, "cannot specify more than 254 instances");
|
|
|
- self.max_instances = instances as DWORD;
|
|
|
+ self.max_instances = instances as u32;
|
|
|
self
|
|
|
}
|
|
|
|
|
@@ -2032,7 +2166,7 @@ impl ServerOptions {
|
|
|
///
|
|
|
/// [`nOutBufferSize`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
|
|
|
pub fn out_buffer_size(&mut self, buffer: u32) -> &mut Self {
|
|
|
- self.out_buffer_size = buffer as DWORD;
|
|
|
+ self.out_buffer_size = buffer;
|
|
|
self
|
|
|
}
|
|
|
|
|
@@ -2042,7 +2176,7 @@ impl ServerOptions {
|
|
|
///
|
|
|
/// [`nInBufferSize`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
|
|
|
pub fn in_buffer_size(&mut self, buffer: u32) -> &mut Self {
|
|
|
- self.in_buffer_size = buffer as DWORD;
|
|
|
+ self.in_buffer_size = buffer;
|
|
|
self
|
|
|
}
|
|
|
|
|
@@ -2099,7 +2233,7 @@ impl ServerOptions {
|
|
|
///
|
|
|
/// [`create`]: ServerOptions::create
|
|
|
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
|
|
- /// [`SECURITY_ATTRIBUTES`]: crate::winapi::um::minwinbase::SECURITY_ATTRIBUTES
|
|
|
+ /// [`SECURITY_ATTRIBUTES`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/Security/struct.SECURITY_ATTRIBUTES.html
|
|
|
pub unsafe fn create_with_security_attributes_raw(
|
|
|
&self,
|
|
|
addr: impl AsRef<OsStr>,
|
|
@@ -2107,7 +2241,7 @@ impl ServerOptions {
|
|
|
) -> io::Result<NamedPipeServer> {
|
|
|
let addr = encode_addr(addr);
|
|
|
|
|
|
- let h = namedpipeapi::CreateNamedPipeW(
|
|
|
+ let h = windows_sys::CreateNamedPipeW(
|
|
|
addr.as_ptr(),
|
|
|
self.open_mode,
|
|
|
self.pipe_mode,
|
|
@@ -2118,11 +2252,11 @@ impl ServerOptions {
|
|
|
attrs as *mut _,
|
|
|
);
|
|
|
|
|
|
- if h == handleapi::INVALID_HANDLE_VALUE {
|
|
|
+ if h == windows_sys::INVALID_HANDLE_VALUE {
|
|
|
return Err(io::Error::last_os_error());
|
|
|
}
|
|
|
|
|
|
- NamedPipeServer::from_raw_handle(h)
|
|
|
+ NamedPipeServer::from_raw_handle(h as _)
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2132,8 +2266,8 @@ impl ServerOptions {
|
|
|
/// See [`ClientOptions::open`].
|
|
|
#[derive(Debug, Clone)]
|
|
|
pub struct ClientOptions {
|
|
|
- desired_access: DWORD,
|
|
|
- security_qos_flags: DWORD,
|
|
|
+ desired_access: u32,
|
|
|
+ security_qos_flags: u32,
|
|
|
}
|
|
|
|
|
|
impl ClientOptions {
|
|
@@ -2152,8 +2286,9 @@ impl ClientOptions {
|
|
|
/// ```
|
|
|
pub fn new() -> Self {
|
|
|
Self {
|
|
|
- desired_access: winnt::GENERIC_READ | winnt::GENERIC_WRITE,
|
|
|
- security_qos_flags: winbase::SECURITY_IDENTIFICATION | winbase::SECURITY_SQOS_PRESENT,
|
|
|
+ desired_access: windows_sys::GENERIC_READ | windows_sys::GENERIC_WRITE,
|
|
|
+ security_qos_flags: windows_sys::SECURITY_IDENTIFICATION
|
|
|
+ | windows_sys::SECURITY_SQOS_PRESENT,
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2164,7 +2299,7 @@ impl ClientOptions {
|
|
|
/// [`GENERIC_READ`]: https://docs.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
|
|
|
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
|
|
pub fn read(&mut self, allowed: bool) -> &mut Self {
|
|
|
- bool_flag!(self.desired_access, allowed, winnt::GENERIC_READ);
|
|
|
+ bool_flag!(self.desired_access, allowed, windows_sys::GENERIC_READ);
|
|
|
self
|
|
|
}
|
|
|
|
|
@@ -2175,7 +2310,7 @@ impl ClientOptions {
|
|
|
/// [`GENERIC_WRITE`]: https://docs.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
|
|
|
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
|
|
pub fn write(&mut self, allowed: bool) -> &mut Self {
|
|
|
- bool_flag!(self.desired_access, allowed, winnt::GENERIC_WRITE);
|
|
|
+ bool_flag!(self.desired_access, allowed, windows_sys::GENERIC_WRITE);
|
|
|
self
|
|
|
}
|
|
|
|
|
@@ -2198,11 +2333,11 @@ impl ClientOptions {
|
|
|
/// automatically when using this method.
|
|
|
///
|
|
|
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
|
|
|
- /// [`SECURITY_IDENTIFICATION`]: crate::winapi::um::winbase::SECURITY_IDENTIFICATION
|
|
|
+ /// [`SECURITY_IDENTIFICATION`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/Storage/FileSystem/constant.SECURITY_IDENTIFICATION.html
|
|
|
/// [Impersonation Levels]: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
|
|
|
pub fn security_qos_flags(&mut self, flags: u32) -> &mut Self {
|
|
|
// See: https://github.com/rust-lang/rust/pull/58216
|
|
|
- self.security_qos_flags = flags | winbase::SECURITY_SQOS_PRESENT;
|
|
|
+ self.security_qos_flags = flags | windows_sys::SECURITY_SQOS_PRESENT;
|
|
|
self
|
|
|
}
|
|
|
|
|
@@ -2227,8 +2362,7 @@ impl ClientOptions {
|
|
|
/// but the server is not currently waiting for a connection. Please see the
|
|
|
/// examples for how to check for this error.
|
|
|
///
|
|
|
- /// [`ERROR_PIPE_BUSY`]: crate::winapi::shared::winerror::ERROR_PIPE_BUSY
|
|
|
- /// [`winapi`]: crate::winapi
|
|
|
+ /// [`ERROR_PIPE_BUSY`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/Foundation/constant.ERROR_PIPE_BUSY.html
|
|
|
/// [enabled I/O]: crate::runtime::Builder::enable_io
|
|
|
/// [Tokio Runtime]: crate::runtime::Runtime
|
|
|
///
|
|
@@ -2239,7 +2373,7 @@ impl ClientOptions {
|
|
|
/// use std::time::Duration;
|
|
|
/// use tokio::net::windows::named_pipe::ClientOptions;
|
|
|
/// use tokio::time;
|
|
|
- /// use winapi::shared::winerror;
|
|
|
+ /// use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY;
|
|
|
///
|
|
|
/// const PIPE_NAME: &str = r"\\.\pipe\mynamedpipe";
|
|
|
///
|
|
@@ -2247,7 +2381,7 @@ impl ClientOptions {
|
|
|
/// let client = loop {
|
|
|
/// match ClientOptions::new().open(PIPE_NAME) {
|
|
|
/// Ok(client) => break client,
|
|
|
- /// Err(e) if e.raw_os_error() == Some(winerror::ERROR_PIPE_BUSY as i32) => (),
|
|
|
+ /// Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (),
|
|
|
/// Err(e) => return Err(e),
|
|
|
/// }
|
|
|
///
|
|
@@ -2277,7 +2411,7 @@ impl ClientOptions {
|
|
|
///
|
|
|
/// [`open`]: ClientOptions::open
|
|
|
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
|
|
- /// [`SECURITY_ATTRIBUTES`]: crate::winapi::um::minwinbase::SECURITY_ATTRIBUTES
|
|
|
+ /// [`SECURITY_ATTRIBUTES`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/Security/struct.SECURITY_ATTRIBUTES.html
|
|
|
pub unsafe fn open_with_security_attributes_raw(
|
|
|
&self,
|
|
|
addr: impl AsRef<OsStr>,
|
|
@@ -2286,28 +2420,28 @@ impl ClientOptions {
|
|
|
let addr = encode_addr(addr);
|
|
|
|
|
|
// NB: We could use a platform specialized `OpenOptions` here, but since
|
|
|
- // we have access to winapi it ultimately doesn't hurt to use
|
|
|
+ // we have access to windows_sys it ultimately doesn't hurt to use
|
|
|
// `CreateFile` explicitly since it allows the use of our already
|
|
|
// well-structured wide `addr` to pass into CreateFileW.
|
|
|
- let h = fileapi::CreateFileW(
|
|
|
+ let h = windows_sys::CreateFileW(
|
|
|
addr.as_ptr(),
|
|
|
self.desired_access,
|
|
|
0,
|
|
|
attrs as *mut _,
|
|
|
- fileapi::OPEN_EXISTING,
|
|
|
+ windows_sys::OPEN_EXISTING,
|
|
|
self.get_flags(),
|
|
|
- ptr::null_mut(),
|
|
|
+ 0,
|
|
|
);
|
|
|
|
|
|
- if h == handleapi::INVALID_HANDLE_VALUE {
|
|
|
+ if h == windows_sys::INVALID_HANDLE_VALUE {
|
|
|
return Err(io::Error::last_os_error());
|
|
|
}
|
|
|
|
|
|
- NamedPipeClient::from_raw_handle(h)
|
|
|
+ NamedPipeClient::from_raw_handle(h as _)
|
|
|
}
|
|
|
|
|
|
fn get_flags(&self) -> u32 {
|
|
|
- self.security_qos_flags | winbase::FILE_FLAG_OVERLAPPED
|
|
|
+ self.security_qos_flags | windows_sys::FILE_FLAG_OVERLAPPED
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2320,16 +2454,19 @@ pub enum PipeMode {
|
|
|
/// Data is written to the pipe as a stream of bytes. The pipe does not
|
|
|
/// distinguish bytes written during different write operations.
|
|
|
///
|
|
|
- /// Corresponds to [`PIPE_TYPE_BYTE`][crate::winapi::um::winbase::PIPE_TYPE_BYTE].
|
|
|
+ /// Corresponds to [`PIPE_TYPE_BYTE`].
|
|
|
+ ///
|
|
|
+ /// [`PIPE_TYPE_BYTE`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/System/Pipes/constant.PIPE_TYPE_BYTE.html
|
|
|
Byte,
|
|
|
/// Data is written to the pipe as a stream of messages. The pipe treats the
|
|
|
/// bytes written during each write operation as a message unit. Any reading
|
|
|
/// on a named pipe returns [`ERROR_MORE_DATA`] when a message is not read
|
|
|
/// completely.
|
|
|
///
|
|
|
- /// Corresponds to [`PIPE_TYPE_MESSAGE`][crate::winapi::um::winbase::PIPE_TYPE_MESSAGE].
|
|
|
+ /// Corresponds to [`PIPE_TYPE_MESSAGE`].
|
|
|
///
|
|
|
- /// [`ERROR_MORE_DATA`]: crate::winapi::shared::winerror::ERROR_MORE_DATA
|
|
|
+ /// [`ERROR_MORE_DATA`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/Foundation/constant.ERROR_MORE_DATA.html
|
|
|
+ /// [`PIPE_TYPE_MESSAGE`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/System/Pipes/constant.PIPE_TYPE_MESSAGE.html
|
|
|
Message,
|
|
|
}
|
|
|
|
|
@@ -2339,11 +2476,15 @@ pub enum PipeMode {
|
|
|
pub enum PipeEnd {
|
|
|
/// The named pipe refers to the client end of a named pipe instance.
|
|
|
///
|
|
|
- /// Corresponds to [`PIPE_CLIENT_END`][crate::winapi::um::winbase::PIPE_CLIENT_END].
|
|
|
+ /// Corresponds to [`PIPE_CLIENT_END`].
|
|
|
+ ///
|
|
|
+ /// [`PIPE_CLIENT_END`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/System/Pipes/constant.PIPE_CLIENT_END.html
|
|
|
Client,
|
|
|
/// The named pipe refers to the server end of a named pipe instance.
|
|
|
///
|
|
|
- /// Corresponds to [`PIPE_SERVER_END`][crate::winapi::um::winbase::PIPE_SERVER_END].
|
|
|
+ /// Corresponds to [`PIPE_SERVER_END`].
|
|
|
+ ///
|
|
|
+ /// [`PIPE_SERVER_END`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/System/Pipes/constant.PIPE_SERVER_END.html
|
|
|
Server,
|
|
|
}
|
|
|
|
|
@@ -2381,26 +2522,26 @@ unsafe fn named_pipe_info(handle: RawHandle) -> io::Result<PipeInfo> {
|
|
|
let mut in_buffer_size = 0;
|
|
|
let mut max_instances = 0;
|
|
|
|
|
|
- let result = namedpipeapi::GetNamedPipeInfo(
|
|
|
- handle,
|
|
|
+ let result = windows_sys::GetNamedPipeInfo(
|
|
|
+ handle as _,
|
|
|
&mut flags,
|
|
|
&mut out_buffer_size,
|
|
|
&mut in_buffer_size,
|
|
|
&mut max_instances,
|
|
|
);
|
|
|
|
|
|
- if result == FALSE {
|
|
|
+ if result == 0 {
|
|
|
return Err(io::Error::last_os_error());
|
|
|
}
|
|
|
|
|
|
let mut end = PipeEnd::Client;
|
|
|
let mut mode = PipeMode::Byte;
|
|
|
|
|
|
- if flags & winbase::PIPE_SERVER_END != 0 {
|
|
|
+ if flags & windows_sys::PIPE_SERVER_END != 0 {
|
|
|
end = PipeEnd::Server;
|
|
|
}
|
|
|
|
|
|
- if flags & winbase::PIPE_TYPE_MESSAGE != 0 {
|
|
|
+ if flags & windows_sys::PIPE_TYPE_MESSAGE != 0 {
|
|
|
mode = PipeMode::Message;
|
|
|
}
|
|
|
|
|
@@ -2412,3 +2553,48 @@ unsafe fn named_pipe_info(handle: RawHandle) -> io::Result<PipeInfo> {
|
|
|
max_instances,
|
|
|
})
|
|
|
}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod test {
|
|
|
+ use self::windows_sys::{PIPE_REJECT_REMOTE_CLIENTS, PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE};
|
|
|
+ use super::*;
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn opts_default_pipe_mode() {
|
|
|
+ let opts = ServerOptions::new();
|
|
|
+ assert_eq!(opts.pipe_mode, PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn opts_unset_reject_remote() {
|
|
|
+ let mut opts = ServerOptions::new();
|
|
|
+ opts.reject_remote_clients(false);
|
|
|
+ assert_eq!(opts.pipe_mode & PIPE_REJECT_REMOTE_CLIENTS, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn opts_set_pipe_mode_maintains_reject_remote_clients() {
|
|
|
+ let mut opts = ServerOptions::new();
|
|
|
+ opts.pipe_mode(PipeMode::Byte);
|
|
|
+ assert_eq!(opts.pipe_mode, PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS);
|
|
|
+
|
|
|
+ opts.reject_remote_clients(false);
|
|
|
+ opts.pipe_mode(PipeMode::Byte);
|
|
|
+ assert_eq!(opts.pipe_mode, PIPE_TYPE_BYTE);
|
|
|
+
|
|
|
+ opts.reject_remote_clients(true);
|
|
|
+ opts.pipe_mode(PipeMode::Byte);
|
|
|
+ assert_eq!(opts.pipe_mode, PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS);
|
|
|
+
|
|
|
+ opts.reject_remote_clients(false);
|
|
|
+ opts.pipe_mode(PipeMode::Message);
|
|
|
+ assert_eq!(opts.pipe_mode, PIPE_TYPE_MESSAGE);
|
|
|
+
|
|
|
+ opts.reject_remote_clients(true);
|
|
|
+ opts.pipe_mode(PipeMode::Message);
|
|
|
+ assert_eq!(
|
|
|
+ opts.pipe_mode,
|
|
|
+ PIPE_TYPE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|