From 31efc2586ae82760e22a5eff9059810896fb1d4e Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Thu, 5 Jan 2023 15:06:21 +0100 Subject: [PATCH 1/4] net: increase named pipe test coverage --- tokio/tests/net_named_pipe.rs | 50 ++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/tokio/tests/net_named_pipe.rs b/tokio/tests/net_named_pipe.rs index c42122465c0..dbf1514b870 100644 --- a/tokio/tests/net_named_pipe.rs +++ b/tokio/tests/net_named_pipe.rs @@ -5,7 +5,7 @@ use std::io; use std::mem; use std::os::windows::io::AsRawHandle; use std::time::Duration; -use tokio::io::AsyncWriteExt; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::windows::named_pipe::{ClientOptions, PipeMode, ServerOptions}; use tokio::time; use windows_sys::Win32::Foundation::{ERROR_NO_DATA, ERROR_PIPE_BUSY, NO_ERROR, UNICODE_STRING}; @@ -327,17 +327,53 @@ async fn test_named_pipe_multi_client_ready() -> io::Result<()> { Ok(()) } -// This tests what happens when a client tries to disconnect. +// This tests that message mode works as expected. #[tokio::test] async fn test_named_pipe_mode_message() -> io::Result<()> { - const PIPE_NAME: &str = r"\\.\pipe\test-named-pipe-mode-message"; + // it's easy to accidentally get a seemingly working test here because byte pipes + // often return contents at write boundaries. to make sure we're doing the right thing we + // explicitly test that it doesn't work in byte mode. + _named_pipe_mode_message(PipeMode::Message).await?; + _named_pipe_mode_message(PipeMode::Byte).await +} + +async fn _named_pipe_mode_message(mode: PipeMode) -> io::Result<()> { + let pipe_name = format!( + r"\\.\pipe\test-named-pipe-mode-message-{}", + matches!(mode, PipeMode::Message) + ); + let mut buf = [0u8; 32]; - let server = ServerOptions::new() - .pipe_mode(PipeMode::Message) - .create(PIPE_NAME)?; + let mut server = ServerOptions::new() + .first_pipe_instance(true) + .pipe_mode(mode) + .create(&pipe_name)?; + + let mut client = ClientOptions::new() + //.pipe_mode(mode) + .open(&pipe_name)?; - let _ = ClientOptions::new().open(PIPE_NAME)?; server.connect().await?; + + // this needs a few iterations, presumably Windows waits for a few calls before merging buffers + for _ in 0..10 { + client.write_all(b"hello").await?; + server.write_all(b"world").await?; + } + for _ in 0..10 { + let n = server.read(&mut buf).await?; + if buf[..n] != b"hello"[..] { + assert!(matches!(mode, PipeMode::Byte)); + return Ok(()); + } + let n = client.read(&mut buf).await?; + if buf[..n] != b"world"[..] { + assert!(matches!(mode, PipeMode::Byte)); + return Ok(()); + } + } + // byte mode should have errored before. + assert!(matches!(mode, PipeMode::Message)); Ok(()) } From 24b5c684e5f6bb1030cd2c4150682738e080dc0e Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Sat, 31 Dec 2022 14:01:58 +0100 Subject: [PATCH 2/4] net: open message-based named pipes with message read mode --- tokio/src/net/windows/named_pipe.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tokio/src/net/windows/named_pipe.rs b/tokio/src/net/windows/named_pipe.rs index 9ede94ea6a0..501f12cbbda 100644 --- a/tokio/src/net/windows/named_pipe.rs +++ b/tokio/src/net/windows/named_pipe.rs @@ -1701,14 +1701,18 @@ impl ServerOptions { /// The default pipe mode is [`PipeMode::Byte`]. See [`PipeMode`] for /// documentation of what each mode means. /// - /// This corresponding to specifying [`dwPipeMode`]. + /// This corresponds to specifying `PIPE_TYPE_` and `PIPE_READMODE_` in [`dwPipeMode`]. /// /// [`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 { 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); + bool_flag!( + self.pipe_mode, + is_msg, + windows_sys::PIPE_TYPE_MESSAGE | windows_sys::PIPE_READMODE_MESSAGE + ); self } @@ -2556,7 +2560,7 @@ unsafe fn named_pipe_info(handle: RawHandle) -> io::Result { #[cfg(test)] mod test { - use self::windows_sys::{PIPE_REJECT_REMOTE_CLIENTS, PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE}; + use self::windows_sys::{PIPE_REJECT_REMOTE_CLIENTS, PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE, PIPE_READMODE_MESSAGE}; use super::*; #[test] @@ -2588,13 +2592,13 @@ mod test { opts.reject_remote_clients(false); opts.pipe_mode(PipeMode::Message); - assert_eq!(opts.pipe_mode, PIPE_TYPE_MESSAGE); + assert_eq!(opts.pipe_mode, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE); opts.reject_remote_clients(true); opts.pipe_mode(PipeMode::Message); assert_eq!( opts.pipe_mode, - PIPE_TYPE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS ); } } From 6d1efe2283a8fd1f16414175c05c7471a676cd58 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Thu, 5 Jan 2023 13:43:48 +0100 Subject: [PATCH 3/4] net: add `ClientOptions.pipe_mode` for named pipes --- tokio/src/net/windows/named_pipe.rs | 29 ++++++++++++++++++++++++++++- tokio/tests/net_named_pipe.rs | 4 +--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/tokio/src/net/windows/named_pipe.rs b/tokio/src/net/windows/named_pipe.rs index 501f12cbbda..83088b68b8e 100644 --- a/tokio/src/net/windows/named_pipe.rs +++ b/tokio/src/net/windows/named_pipe.rs @@ -2272,6 +2272,7 @@ impl ServerOptions { pub struct ClientOptions { desired_access: u32, security_qos_flags: u32, + pipe_mode: PipeMode, } impl ClientOptions { @@ -2293,6 +2294,7 @@ impl ClientOptions { desired_access: windows_sys::GENERIC_READ | windows_sys::GENERIC_WRITE, security_qos_flags: windows_sys::SECURITY_IDENTIFICATION | windows_sys::SECURITY_SQOS_PRESENT, + pipe_mode: PipeMode::Byte, } } @@ -2345,6 +2347,15 @@ impl ClientOptions { self } + /// The pipe mode. + /// + /// The default pipe mode is [`PipeMode::Byte`]. See [`PipeMode`] for + /// documentation of what each mode means. + pub fn pipe_mode(&mut self, pipe_mode: PipeMode) -> &mut Self { + self.pipe_mode = pipe_mode; + self + } + /// Opens the named pipe identified by `addr`. /// /// This opens the client using [`CreateFile`] with the @@ -2441,6 +2452,20 @@ impl ClientOptions { return Err(io::Error::last_os_error()); } + if matches!(self.pipe_mode, PipeMode::Message) { + let mut mode = windows_sys::PIPE_READMODE_MESSAGE; + let result = windows_sys::SetNamedPipeHandleState( + h, + &mut mode, + ptr::null_mut(), + ptr::null_mut(), + ); + + if result == 0 { + return Err(io::Error::last_os_error()); + } + } + NamedPipeClient::from_raw_handle(h as _) } @@ -2560,7 +2585,9 @@ unsafe fn named_pipe_info(handle: RawHandle) -> io::Result { #[cfg(test)] mod test { - use self::windows_sys::{PIPE_REJECT_REMOTE_CLIENTS, PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE, PIPE_READMODE_MESSAGE}; + use self::windows_sys::{ + PIPE_READMODE_MESSAGE, PIPE_REJECT_REMOTE_CLIENTS, PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE, + }; use super::*; #[test] diff --git a/tokio/tests/net_named_pipe.rs b/tokio/tests/net_named_pipe.rs index dbf1514b870..3ddc4c8a9bf 100644 --- a/tokio/tests/net_named_pipe.rs +++ b/tokio/tests/net_named_pipe.rs @@ -349,9 +349,7 @@ async fn _named_pipe_mode_message(mode: PipeMode) -> io::Result<()> { .pipe_mode(mode) .create(&pipe_name)?; - let mut client = ClientOptions::new() - //.pipe_mode(mode) - .open(&pipe_name)?; + let mut client = ClientOptions::new().pipe_mode(mode).open(&pipe_name)?; server.connect().await?; From c582f04c614b3433b461936715edaed2774ecb7c Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Tue, 31 Jan 2023 22:39:39 +0100 Subject: [PATCH 4/4] show that PIPE_READMOE_MESSAGE is cleared --- tokio/src/net/windows/named_pipe.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tokio/src/net/windows/named_pipe.rs b/tokio/src/net/windows/named_pipe.rs index 83088b68b8e..2107c1cdfce 100644 --- a/tokio/src/net/windows/named_pipe.rs +++ b/tokio/src/net/windows/named_pipe.rs @@ -2627,5 +2627,8 @@ mod test { opts.pipe_mode, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS ); + + opts.pipe_mode(PipeMode::Byte); + assert_eq!(opts.pipe_mode, PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS); } }