Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions library/std/src/net/hostname.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::ffi::OsString;

/// Returns the system hostname.
///
/// This can error out in platform-specific error cases;
/// for example, uefi and wasm, where hostnames aren't
/// supported.
///
/// # Underlying system calls
///
/// | Platform | System call |
/// |----------|---------------------------------------------------------------------------------------------------------|
/// | UNIX | [`gethostname`](https://www.man7.org/linux/man-pages/man2/gethostname.2.html) |
/// | Windows | [`GetHostNameW`](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-gethostnamew) |
///
/// Note that platform-specific behavior [may change in the future][changes].
///
/// [changes]: crate::io#platform-specific-behavior
#[unstable(feature = "gethostname", issue = "135142")]
pub fn hostname() -> crate::io::Result<OsString> {
crate::sys::net::hostname()
}
6 changes: 5 additions & 1 deletion library/std/src/net/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Networking primitives for TCP/UDP communication.
//!
//! This module provides networking functionality for the Transmission Control and User
//! Datagram Protocols, as well as types for IP and socket addresses.
//! Datagram Protocols, as well as types for IP and socket addresses and functions related
//! to network properties.
//!
//! # Organization
//!
Expand All @@ -24,6 +25,8 @@
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::net::AddrParseError;

#[unstable(feature = "gethostname", issue = "135142")]
pub use self::hostname::hostname;
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope};
#[stable(feature = "rust1", since = "1.0.0")]
Expand All @@ -35,6 +38,7 @@ pub use self::tcp::{Incoming, TcpListener, TcpStream};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::udp::UdpSocket;

mod hostname;
mod ip_addr;
mod socket_addr;
mod tcp;
Expand Down
80 changes: 3 additions & 77 deletions library/std/src/sys/net/connection/socket/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ use crate::net::{Shutdown, SocketAddr};
use crate::os::windows::io::{
AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket,
};
use crate::sync::atomic::Atomic;
use crate::sync::atomic::Ordering::{AcqRel, Relaxed};
use crate::sys::c;
use crate::sys::pal::winsock::last_error;
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::time::Duration;
use crate::{cmp, mem, ptr, sys};
Expand Down Expand Up @@ -112,84 +111,11 @@ pub(super) mod netc {
}
}

pub use crate::sys::pal::winsock::{cleanup, cvt, cvt_gai, cvt_r, startup as init};

#[expect(missing_debug_implementations)]
pub struct Socket(OwnedSocket);

static WSA_INITIALIZED: Atomic<bool> = Atomic::<bool>::new(false);

/// Checks whether the Windows socket interface has been started already, and
/// if not, starts it.
#[inline]
pub fn init() {
if !WSA_INITIALIZED.load(Relaxed) {
wsa_startup();
}
}

#[cold]
fn wsa_startup() {
unsafe {
let mut data: c::WSADATA = mem::zeroed();
let ret = c::WSAStartup(
0x202, // version 2.2
&mut data,
);
assert_eq!(ret, 0);
if WSA_INITIALIZED.swap(true, AcqRel) {
// If another thread raced with us and called WSAStartup first then call
// WSACleanup so it's as though WSAStartup was only called once.
c::WSACleanup();
}
}
}

pub fn cleanup() {
// We don't need to call WSACleanup here because exiting the process will cause
// the OS to clean everything for us, which is faster than doing it manually.
// See #141799.
}

/// Returns the last error from the Windows socket interface.
fn last_error() -> io::Error {
io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() })
}

#[doc(hidden)]
pub trait IsMinusOne {
fn is_minus_one(&self) -> bool;
}

macro_rules! impl_is_minus_one {
($($t:ident)*) => ($(impl IsMinusOne for $t {
fn is_minus_one(&self) -> bool {
*self == -1
}
})*)
}

impl_is_minus_one! { i8 i16 i32 i64 isize }

/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1)
/// and if so, returns the last error from the Windows socket interface. This
/// function must be called before another call to the socket API is made.
pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
if t.is_minus_one() { Err(last_error()) } else { Ok(t) }
}

/// A variant of `cvt` for `getaddrinfo` which return 0 for a success.
pub fn cvt_gai(err: c_int) -> io::Result<()> {
if err == 0 { Ok(()) } else { Err(last_error()) }
}

/// Just to provide the same interface as sys/pal/unix/net.rs
pub fn cvt_r<T, F>(mut f: F) -> io::Result<T>
where
T: IsMinusOne,
F: FnMut() -> T,
{
cvt(f())
}

impl Socket {
pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
let family = match *addr {
Expand Down
14 changes: 14 additions & 0 deletions library/std/src/sys/net/hostname/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
cfg_select! {
target_family = "unix" => {
mod unix;
pub use unix::hostname;
}
target_os = "windows" => {
mod windows;
pub use windows::hostname;
}
_ => {
mod unsupported;
pub use unsupported::hostname;
}
}
62 changes: 62 additions & 0 deletions library/std/src/sys/net/hostname/unix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use crate::ffi::OsString;
use crate::io;
use crate::os::unix::ffi::OsStringExt;
use crate::sys::pal::os::errno;

pub fn hostname() -> io::Result<OsString> {
// Query the system for the maximum host name length.
let host_name_max = match unsafe { libc::sysconf(libc::_SC_HOST_NAME_MAX) } {
// If this fails (possibly because there is no maximum length), then
// assume a maximum length of _POSIX_HOST_NAME_MAX (255).
-1 => 255,
max => max as usize,
};

// Reserve space for the nul terminator too.
let mut buf = Vec::<u8>::try_with_capacity(host_name_max + 1)?;
loop {
// SAFETY: `buf.capacity()` bytes of `buf` are writable.
let r = unsafe { libc::gethostname(buf.as_mut_ptr().cast(), buf.capacity()) };
match (r != 0).then(errno) {
None => {
// Unfortunately, the UNIX specification says that the name will
// be truncated if it does not fit in the buffer, without returning
// an error. As additionally, the truncated name may still be null-
// terminated, there is no reliable way to detect truncation.
// Fortunately, most platforms ignore what the specification says
// and return an error (mostly ENAMETOOLONG). Should that not be
// the case, the following detects truncation if the null-terminator
// was omitted. Note that this check does not impact performance at
// all as we need to find the length of the string anyways.
//
// Use `strnlen` as it does not place an initialization requirement
// on the bytes after the nul terminator.
//
// SAFETY: `buf.capacity()` bytes of `buf` are accessible, and are
// initialized up to and including a possible nul terminator.
let len = unsafe { libc::strnlen(buf.as_ptr().cast(), buf.capacity()) };
if len < buf.capacity() {
// If the string is nul-terminated, we assume that is has not
// been truncated, as the capacity *should be* enough to hold
// `HOST_NAME_MAX` bytes.
// SAFETY: `len + 1` bytes have been initialized (we exclude
// the nul terminator from the string).
unsafe { buf.set_len(len) };
return Ok(OsString::from_vec(buf));
}
}
// As `buf.capacity()` is always less than or equal to `isize::MAX`
// (Rust allocations cannot exceed that limit), the only way `EINVAL`
// can be returned is if the system uses `EINVAL` to report that the
// name does not fit in the provided buffer. In that case (or in the
// case of `ENAMETOOLONG`), resize the buffer and try again.
Some(libc::EINVAL | libc::ENAMETOOLONG) => {}
// Other error codes (e.g. EPERM) have nothing to do with the buffer
// size and should be returned to the user.
Some(err) => return Err(io::Error::from_raw_os_error(err)),
}

// Resize the buffer (according to `Vec`'s resizing rules) and try again.
buf.try_reserve(buf.capacity() + 1)?;
}
}
6 changes: 6 additions & 0 deletions library/std/src/sys/net/hostname/unsupported.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use crate::ffi::OsString;
use crate::io::{Error, Result};

pub fn hostname() -> Result<OsString> {
Err(Error::UNSUPPORTED_PLATFORM)
}
24 changes: 24 additions & 0 deletions library/std/src/sys/net/hostname/windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use crate::ffi::OsString;
use crate::io::Result;
use crate::mem::MaybeUninit;
use crate::os::windows::ffi::OsStringExt;
use crate::sys::pal::c;
use crate::sys::pal::winsock::{self, cvt};

pub fn hostname() -> Result<OsString> {
winsock::startup();

// The documentation of GetHostNameW says that a buffer size of 256 is
// always enough.
let mut buffer = [const { MaybeUninit::<u16>::uninit() }; 256];
// SAFETY: these parameters specify a valid, writable region of memory.
cvt(unsafe { c::GetHostNameW(buffer.as_mut_ptr().cast(), buffer.len() as i32) })?;
// Use `lstrlenW` here as it does not require the bytes after the nul
// terminator to be initialized.
// SAFETY: if `GetHostNameW` returns successfully, the name is nul-terminated.
let len = unsafe { c::lstrlenW(buffer.as_ptr().cast()) };
// SAFETY: the length of the name is `len`, hence `len` bytes have been
// initialized by `GetHostNameW`.
let name = unsafe { buffer[..len as usize].assume_init_ref() };
Ok(OsString::from_wide(name))
}
3 changes: 3 additions & 0 deletions library/std/src/sys/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
/// `UdpSocket` as well as related functionality like DNS resolving.
mod connection;
pub use connection::*;

mod hostname;
pub use hostname::hostname;
2 changes: 2 additions & 0 deletions library/std/src/sys/pal/windows/c/bindings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2170,6 +2170,7 @@ GetFileType
GETFINALPATHNAMEBYHANDLE_FLAGS
GetFinalPathNameByHandleW
GetFullPathNameW
GetHostNameW
GetLastError
GetModuleFileNameW
GetModuleHandleA
Expand Down Expand Up @@ -2270,6 +2271,7 @@ LPPROGRESS_ROUTINE
LPPROGRESS_ROUTINE_CALLBACK_REASON
LPTHREAD_START_ROUTINE
LPWSAOVERLAPPED_COMPLETION_ROUTINE
lstrlenW
M128A
MAX_PATH
MAXIMUM_REPARSE_DATA_BUFFER_SIZE
Expand Down
2 changes: 2 additions & 0 deletions library/std/src/sys/pal/windows/c/windows_sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetFileSizeEx(hfile : HANDLE,
windows_targets::link!("kernel32.dll" "system" fn GetFileType(hfile : HANDLE) -> FILE_TYPE);
windows_targets::link!("kernel32.dll" "system" fn GetFinalPathNameByHandleW(hfile : HANDLE, lpszfilepath : PWSTR, cchfilepath : u32, dwflags : GETFINALPATHNAMEBYHANDLE_FLAGS) -> u32);
windows_targets::link!("kernel32.dll" "system" fn GetFullPathNameW(lpfilename : PCWSTR, nbufferlength : u32, lpbuffer : PWSTR, lpfilepart : *mut PWSTR) -> u32);
windows_targets::link!("ws2_32.dll" "system" fn GetHostNameW(name : PWSTR, namelen : i32) -> i32);
windows_targets::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR);
windows_targets::link!("kernel32.dll" "system" fn GetModuleFileNameW(hmodule : HMODULE, lpfilename : PWSTR, nsize : u32) -> u32);
windows_targets::link!("kernel32.dll" "system" fn GetModuleHandleA(lpmodulename : PCSTR) -> HMODULE);
Expand Down Expand Up @@ -134,6 +135,7 @@ windows_targets::link!("ws2_32.dll" "system" fn getsockname(s : SOCKET, name : *
windows_targets::link!("ws2_32.dll" "system" fn getsockopt(s : SOCKET, level : i32, optname : i32, optval : PSTR, optlen : *mut i32) -> i32);
windows_targets::link!("ws2_32.dll" "system" fn ioctlsocket(s : SOCKET, cmd : i32, argp : *mut u32) -> i32);
windows_targets::link!("ws2_32.dll" "system" fn listen(s : SOCKET, backlog : i32) -> i32);
windows_targets::link!("kernel32.dll" "system" fn lstrlenW(lpstring : PCWSTR) -> i32);
windows_targets::link!("ws2_32.dll" "system" fn recv(s : SOCKET, buf : PSTR, len : i32, flags : SEND_RECV_FLAGS) -> i32);
windows_targets::link!("ws2_32.dll" "system" fn recvfrom(s : SOCKET, buf : PSTR, len : i32, flags : i32, from : *mut SOCKADDR, fromlen : *mut i32) -> i32);
windows_targets::link!("ws2_32.dll" "system" fn select(nfds : i32, readfds : *mut FD_SET, writefds : *mut FD_SET, exceptfds : *mut FD_SET, timeout : *const TIMEVAL) -> i32);
Expand Down
1 change: 1 addition & 0 deletions library/std/src/sys/pal/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ cfg_select! {
pub use self::stack_overflow_uwp as stack_overflow;
}
}
pub mod winsock;

/// Map a [`Result<T, WinError>`] to [`io::Result<T>`](crate::io::Result<T>).
pub trait IoResult<T> {
Expand Down
80 changes: 80 additions & 0 deletions library/std/src/sys/pal/windows/winsock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use super::c;
use crate::ffi::c_int;
use crate::sync::atomic::Atomic;
use crate::sync::atomic::Ordering::{AcqRel, Relaxed};
use crate::{io, mem};

static WSA_STARTED: Atomic<bool> = Atomic::<bool>::new(false);

/// Checks whether the Windows socket interface has been started already, and
/// if not, starts it.
#[inline]
pub fn startup() {
if !WSA_STARTED.load(Relaxed) {
wsa_startup();
}
}

#[cold]
fn wsa_startup() {
unsafe {
let mut data: c::WSADATA = mem::zeroed();
let ret = c::WSAStartup(
0x202, // version 2.2
&mut data,
);
assert_eq!(ret, 0);
if WSA_STARTED.swap(true, AcqRel) {
// If another thread raced with us and called WSAStartup first then call
// WSACleanup so it's as though WSAStartup was only called once.
c::WSACleanup();
}
}
}

pub fn cleanup() {
// We don't need to call WSACleanup here because exiting the process will cause
// the OS to clean everything for us, which is faster than doing it manually.
// See #141799.
}

/// Returns the last error from the Windows socket interface.
pub fn last_error() -> io::Error {
io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() })
}

#[doc(hidden)]
pub trait IsMinusOne {
fn is_minus_one(&self) -> bool;
}

macro_rules! impl_is_minus_one {
($($t:ident)*) => ($(impl IsMinusOne for $t {
fn is_minus_one(&self) -> bool {
*self == -1
}
})*)
}

impl_is_minus_one! { i8 i16 i32 i64 isize }

/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1)
/// and if so, returns the last error from the Windows socket interface. This
/// function must be called before another call to the socket API is made.
pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
if t.is_minus_one() { Err(last_error()) } else { Ok(t) }
}

/// A variant of `cvt` for `getaddrinfo` which return 0 for a success.
pub fn cvt_gai(err: c_int) -> io::Result<()> {
if err == 0 { Ok(()) } else { Err(last_error()) }
}

/// Just to provide the same interface as sys/pal/unix/net.rs
pub fn cvt_r<T, F>(mut f: F) -> io::Result<T>
where
T: IsMinusOne,
F: FnMut() -> T,
{
cvt(f())
}
Loading