use std::io::{self, Read, Write};
use std::sync::Arc;
use windows::Win32::Foundation::{
CloseHandle, ERROR_BROKEN_PIPE, ERROR_FILE_NOT_FOUND, ERROR_IO_PENDING, ERROR_PIPE_BUSY,
ERROR_PIPE_CONNECTED, ERROR_PIPE_NOT_CONNECTED, GENERIC_READ, GENERIC_WRITE, HANDLE,
};
use windows::Win32::Storage::FileSystem::{
CreateFileW, FILE_FLAG_OVERLAPPED, FILE_SHARE_MODE, OPEN_EXISTING, PIPE_ACCESS_DUPLEX,
ReadFile, WriteFile,
};
use windows::Win32::System::IO::{GetOverlappedResult, OVERLAPPED};
use windows::Win32::System::Pipes::{
ConnectNamedPipe, CreateNamedPipeW, PIPE_READMODE_BYTE, PIPE_TYPE_BYTE,
PIPE_UNLIMITED_INSTANCES, PIPE_WAIT, WaitNamedPipeW,
};
use windows::Win32::System::Threading::{CreateEventW, INFINITE, WaitForSingleObject};
use windows::core::PCWSTR;
use crate::PIPE_PREFIX;
const PIPE_BUFFER: u32 = 64 * 1024;
pub fn user_pipe_name() -> String {
let user = std::env::var("USERNAME").unwrap_or_else(|_| "default".to_string());
format!("{PIPE_PREFIX}-{user}")
}
fn to_wide(s: &str) -> Vec<u16> {
s.encode_utf16().chain(std::iter::once(0)).collect()
}
fn last_os_code() -> u32 {
io::Error::last_os_error().raw_os_error().unwrap_or(0) as u32
}
pub struct PipeConn {
handle: HANDLE,
}
unsafe impl Send for PipeConn {}
unsafe impl Sync for PipeConn {}
impl PipeConn {
fn read_impl(&self, buf: &mut [u8]) -> io::Result<usize> {
unsafe {
overlapped_op(self.handle, |ov| {
ReadFile(self.handle, Some(buf), None, Some(ov))
})
}
}
fn write_impl(&self, buf: &[u8]) -> io::Result<usize> {
unsafe {
overlapped_op(self.handle, |ov| {
WriteFile(self.handle, Some(buf), None, Some(ov))
})
}
}
}
unsafe fn overlapped_op<F>(handle: HANDLE, start: F) -> io::Result<usize>
where
F: FnOnce(*mut OVERLAPPED) -> windows::core::Result<()>,
{
unsafe {
let event = CreateEventW(None, true, false, PCWSTR::null())
.map_err(|_| io::Error::from_raw_os_error(last_os_code() as i32))?;
let mut ov = OVERLAPPED {
hEvent: event,
..Default::default()
};
let started = start(&mut ov);
let result = match started {
Ok(()) => finish(handle, &ov),
Err(_) => {
let code = last_os_code();
if code == ERROR_IO_PENDING.0 {
WaitForSingleObject(event, INFINITE);
finish(handle, &ov)
} else {
eof_or_err(code)
}
}
};
let _ = CloseHandle(event);
result
}
}
unsafe fn finish(handle: HANDLE, ov: &OVERLAPPED) -> io::Result<usize> {
unsafe {
let mut transferred = 0u32;
match GetOverlappedResult(handle, ov, &mut transferred, false) {
Ok(()) => Ok(transferred as usize),
Err(_) => eof_or_err(last_os_code()),
}
}
}
fn eof_or_err(code: u32) -> io::Result<usize> {
if code == ERROR_BROKEN_PIPE.0 || code == ERROR_PIPE_NOT_CONNECTED.0 {
Ok(0)
} else {
Err(io::Error::from_raw_os_error(code as i32))
}
}
impl Drop for PipeConn {
fn drop(&mut self) {
unsafe {
let _ = CloseHandle(self.handle);
}
}
}
impl Read for &PipeConn {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.read_impl(buf)
}
}
impl Write for &PipeConn {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.write_impl(buf)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Read for PipeConn {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.read_impl(buf)
}
}
impl Write for PipeConn {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.write_impl(buf)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
pub fn connect(name: &str) -> io::Result<PipeConn> {
let wide = to_wide(name);
let access = GENERIC_READ.0 | GENERIC_WRITE.0;
loop {
let res = unsafe {
CreateFileW(
PCWSTR(wide.as_ptr()),
access,
FILE_SHARE_MODE(0),
None,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
None,
)
};
match res {
Ok(handle) if !handle.is_invalid() => return Ok(PipeConn { handle }),
_ => {
let code = last_os_code();
if code == ERROR_PIPE_BUSY.0 {
let _ = unsafe { WaitNamedPipeW(PCWSTR(wide.as_ptr()), 2000) };
continue;
}
if code == ERROR_FILE_NOT_FOUND.0 {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"aucun serveur wimux à l'écoute",
));
}
return Err(io::Error::from_raw_os_error(code as i32));
}
}
}
}
pub struct PipeListener {
wide_name: Vec<u16>,
}
impl PipeListener {
pub fn bind(name: &str) -> Self {
PipeListener {
wide_name: to_wide(name),
}
}
pub fn accept(&self) -> io::Result<PipeConn> {
let handle = unsafe {
CreateNamedPipeW(
PCWSTR(self.wide_name.as_ptr()),
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
PIPE_BUFFER,
PIPE_BUFFER,
0,
None,
)
};
if handle.is_invalid() {
return Err(io::Error::from_raw_os_error(last_os_code() as i32));
}
let event = unsafe { CreateEventW(None, true, false, PCWSTR::null()) }
.map_err(|_| io::Error::from_raw_os_error(last_os_code() as i32))?;
let mut ov = OVERLAPPED {
hEvent: event,
..Default::default()
};
let connect_res = unsafe { ConnectNamedPipe(handle, Some(&mut ov)) };
let outcome = match connect_res {
Ok(()) => Ok(()),
Err(_) => {
let code = last_os_code();
if code == ERROR_IO_PENDING.0 {
unsafe { WaitForSingleObject(event, INFINITE) };
let mut transferred = 0u32;
unsafe { GetOverlappedResult(handle, &ov, &mut transferred, false) }
.map_err(|_| io::Error::from_raw_os_error(last_os_code() as i32))
} else if code == ERROR_PIPE_CONNECTED.0 {
Ok(())
} else {
Err(io::Error::from_raw_os_error(code as i32))
}
}
};
unsafe {
let _ = CloseHandle(event);
}
match outcome {
Ok(()) => Ok(PipeConn { handle }),
Err(e) => {
unsafe {
let _ = CloseHandle(handle);
}
Err(e)
}
}
}
}
pub fn shared(conn: PipeConn) -> Arc<PipeConn> {
Arc::new(conn)
}