Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

from_raw_socket does not call WSAStartup #115683

Open
ahcodedthat opened this issue Sep 8, 2023 · 1 comment
Open

from_raw_socket does not call WSAStartup #115683

ahcodedthat opened this issue Sep 8, 2023 · 1 comment
Labels
A-io Area: std::io, std::fs, std::net and std::path C-bug Category: This is a bug. O-windows Operating system: Windows T-libs Relevant to the library team, which will review and decide on the PR/issue.

Comments

@ahcodedthat
Copy link

I was working on some code in which a socket is inherited by a child process from its parent, much like how BSD inetd works. This works without incident on Unix-like platforms.

Windows also supports socket inheritance, but there's a catch: even though the socket already exists from the moment the process starts, WSAStartup must still be called before using it.

The issue is that the standard library's implementations of FromRawSocket::from_raw_socket do not trigger a call to WSAStartup. The standard library does call WSAStartup as soon as it's used to create a socket (e.g. with TcpListener::bind), but not when bringing in an existing, inherited socket.

Please consider calling WSAStartup from from_raw_socket as well. Note, though, that it would no longer be zero-cost. This is acceptable to me, but I don't know if others would have a problem with that.

Here's a pair of example programs that demonstrate socket inheritance on Windows.

# Cargo.toml

[package]
name = "socket-inheritance-example"
version = "0.1.0"
edition = "2021"

[dependencies]
windows-sys = { version = "0.48.0", features = ["Win32_Foundation"] }
// src/bin/parent.rs

use std::{
	env,
	io::{self, Read},
	net::{Ipv4Addr, TcpListener, TcpStream},
	os::windows::io::{AsRawSocket, OwnedSocket},
	process::Command,
};
use windows_sys::Win32::Foundation::{SetHandleInformation, HANDLE_FLAG_INHERIT};

fn main() {
	// Get the child program's name from the command line.
	let inheritor_program = env::args().skip(1).next().unwrap();

	// Open a TCP listening socket.
	let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0u16)).unwrap();
	let address = listener.local_addr().unwrap();
	let listener: OwnedSocket = listener.into();

	// Make the TCP listening socket inheritable.
	let made_inheritable = unsafe {
		SetHandleInformation(
			listener.as_raw_socket() as _,
			HANDLE_FLAG_INHERIT,
			HANDLE_FLAG_INHERIT,
		)
	};

	if made_inheritable == 0 {
		panic!("{}", io::Error::last_os_error());
	}

	// Start the child process.
	Command::new(inheritor_program)
	.arg(format!("{}", listener.as_raw_socket()))
	.spawn()
	.unwrap();

	// Connect to the child process and read some bytes.
	let mut message = Vec::<u8>::new();
	{
		let mut connection = TcpStream::connect(address).unwrap();
		connection.read_to_end(&mut message).unwrap();
	}

	// Write them to standard output.
	let message = String::from_utf8(message).unwrap();
	print!("{message}");
}
// src/bin/child.rs

use std::{
	env,
	io::Write,
	net::{Ipv4Addr, TcpListener},
	os::windows::io::{FromRawSocket, RawSocket},
};

fn main() {
	// drop(TcpListener::bind((Ipv4Addr::LOCALHOST, 0u16)));

	let socket_handle: RawSocket = env::args().skip(1).next().unwrap().parse().unwrap();
	let socket = unsafe { TcpListener::from_raw_socket(socket_handle) };
	let (mut connection, _) = socket.accept().unwrap();
	connection.write_all(b"Hello, world!\n").unwrap();
	connection.flush().unwrap();
}

To try it, run cargo build --bins, then run target\debug\parent target\debug\child.

Unless that first drop statement is uncommented, the child process will panic with the error message “Either the application has not called WSAStartup, or WSAStartup failed.”

Meta

rustc --version --verbose:

rustc 1.74.0-nightly (1e746d774 2023-09-07)
binary: rustc
commit-hash: 1e746d7741d44551e9378daf13b8797322aa0b74
commit-date: 2023-09-07
host: x86_64-pc-windows-msvc
release: 1.74.0-nightly
LLVM version: 17.0.0
Backtrace

thread 'main' panicked at src\bin\inheritor.rs:13:47:
called `Result::unwrap()` on an `Err` value: Os { code: 10093, kind: Uncategorized, message: "Either the applicat
ion has not called WSAStartup, or WSAStartup failed." }
stack backtrace:
   0: std::panicking::begin_panic_handler
             at /rustc/1e746d7741d44551e9378daf13b8797322aa0b74/library\std\src\panicking.rs:619
   1: core::panicking::panic_fmt
             at /rustc/1e746d7741d44551e9378daf13b8797322aa0b74/library\core\src\panicking.rs:72
   2: core::result::unwrap_failed
             at /rustc/1e746d7741d44551e9378daf13b8797322aa0b74/library\core\src\result.rs:1652
   3: enum2$<core::result::Result<tuple$<std::net::tcp::TcpStream,enum2$<core::net::socket_addr::SocketAddr> >,st
d::io::error::Error> >::unwrap<tuple$<std::net::tcp::TcpStream,enum2$<core::net::socket_addr::SocketAddr> >,std::
io::error::Error>
             at /rustc/1e746d7741d44551e9378daf13b8797322aa0b74\library\core\src\result.rs:1077
   4: inheritor::main
             at C:\Users\ahcodedthat\Desktop\socket-inheritance-example\src\bin\inheritor.rs:13
   5: core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
             at /rustc/1e746d7741d44551e9378daf13b8797322aa0b74\library\core\src\ops\function.rs:250
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

@ahcodedthat ahcodedthat added the C-bug Category: This is a bug. label Sep 8, 2023
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Sep 8, 2023
@ChrisDenton ChrisDenton added O-windows Operating system: Windows T-libs Relevant to the library team, which will review and decide on the PR/issue. A-io Area: std::io, std::fs, std::net and std::path and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Sep 9, 2023
@ChrisDenton
Copy link
Contributor

I guess the alternative would be to ensure WSAStartup is called before any socket using function. Maybe by wrapping the socket API so it can't be forgotten.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-io Area: std::io, std::fs, std::net and std::path C-bug Category: This is a bug. O-windows Operating system: Windows T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

3 participants