From 6d9b4000cc7c493ae7b4bdf6f415eba7d2e51986 Mon Sep 17 00:00:00 2001 From: Dallas Strouse Date: Mon, 6 Jan 2025 12:18:37 -0600 Subject: [PATCH 1/2] std: net: Add support for getting the system hostname --- library/std/src/net/hostname.rs | 11 ++++++++ library/std/src/net/mod.rs | 6 ++++- library/std/src/sys/pal/unsupported/net.rs | 5 ++++ .../std/src/sys/pal/windows/c/bindings.txt | 1 + .../std/src/sys/pal/windows/c/windows_sys.rs | 1 + library/std/src/sys/pal/windows/net.rs | 4 +-- library/std/src/sys_common/net.rs | 27 ++++++++++++++++++- 7 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 library/std/src/net/hostname.rs diff --git a/library/std/src/net/hostname.rs b/library/std/src/net/hostname.rs new file mode 100644 index 0000000000000..3e236086af752 --- /dev/null +++ b/library/std/src/net/hostname.rs @@ -0,0 +1,11 @@ +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. +#[unstable(feature = "gethostname", issue = "135142")] +pub fn hostname() -> crate::io::Result { + crate::sys_common::net::gethostname() +} diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index ddd3b68dd2d63..6bece46626143 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -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 //! @@ -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")] @@ -36,6 +39,7 @@ pub use self::tcp::{Incoming, TcpListener, TcpStream}; pub use self::udp::UdpSocket; use crate::io::{self, ErrorKind}; +mod hostname; mod ip_addr; mod socket_addr; mod tcp; diff --git a/library/std/src/sys/pal/unsupported/net.rs b/library/std/src/sys/pal/unsupported/net.rs index 87e6106468fdb..1c3010ce4b639 100644 --- a/library/std/src/sys/pal/unsupported/net.rs +++ b/library/std/src/sys/pal/unsupported/net.rs @@ -1,9 +1,14 @@ +use crate::ffi::OsString; use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::sys::unsupported; use crate::time::Duration; +pub fn gethostname() -> crate::io::Result { + unsupported() +} + pub struct TcpStream(!); impl TcpStream { diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index c06f274685c24..3e9b2f10453ba 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -1978,6 +1978,7 @@ Windows.Win32.Networking.WinSock.FD_SET Windows.Win32.Networking.WinSock.FIONBIO Windows.Win32.Networking.WinSock.freeaddrinfo Windows.Win32.Networking.WinSock.getaddrinfo +Windows.Win32.Networking.WinSock.gethostname Windows.Win32.Networking.WinSock.getpeername Windows.Win32.Networking.WinSock.getsockname Windows.Win32.Networking.WinSock.getsockopt diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 79513d33a1ac7..bcf0b090ae28e 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -126,6 +126,7 @@ windows_targets::link!("ws2_32.dll" "system" fn closesocket(s : SOCKET) -> i32); windows_targets::link!("ws2_32.dll" "system" fn connect(s : SOCKET, name : *const SOCKADDR, namelen : i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn freeaddrinfo(paddrinfo : *const ADDRINFOA)); windows_targets::link!("ws2_32.dll" "system" fn getaddrinfo(pnodename : PCSTR, pservicename : PCSTR, phints : *const ADDRINFOA, ppresult : *mut *mut ADDRINFOA) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn gethostname(name : PSTR, namelen : i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn getpeername(s : SOCKET, name : *mut SOCKADDR, namelen : *mut i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn getsockname(s : SOCKET, name : *mut SOCKADDR, namelen : *mut i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn getsockopt(s : SOCKET, level : i32, optname : i32, optval : PSTR, optlen : *mut i32) -> i32); diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs index a92853c642c06..8e2ea4fe5d3ac 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/pal/windows/net.rs @@ -31,8 +31,8 @@ pub mod netc { IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, IPPROTO_IP, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, SO_BROADCAST, SO_RCVTIMEO, SO_SNDTIMEO, SOCK_DGRAM, SOCK_STREAM, SOCKADDR as sockaddr, - SOCKADDR_STORAGE as sockaddr_storage, SOL_SOCKET, bind, connect, freeaddrinfo, getpeername, - getsockname, getsockopt, listen, setsockopt, + SOCKADDR_STORAGE as sockaddr_storage, SOL_SOCKET, bind, connect, freeaddrinfo, gethostname, + getpeername, getsockname, getsockopt, listen, setsockopt, }; #[allow(non_camel_case_types)] diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 74306978d2284..36dc3cf63bd26 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests; -use crate::ffi::{c_int, c_void}; +use crate::ffi::{CStr, OsString, c_char, c_int, c_void}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::sys::common::small_c_string::run_with_cstr; @@ -215,6 +215,31 @@ impl<'a> TryFrom<(&'a str, u16)> for LookupHost { } } +//////////////////////////////////////////////////////////////////////////////// +// gethostname +//////////////////////////////////////////////////////////////////////////////// + +pub fn gethostname() -> crate::io::Result { + init(); + // 255 bytes is the maximum allowable length for a hostname (as per the DNS spec), + // so we shouldn't ever have problems with this. I (@orowith2os) considered using a constant + // and letting the platform set the length, but it was determined after some discussion that + // this could break things if the platform changes their length. Possible alternative is to + // read the sysconf setting for the max hostname length, but that might be a bit too much work. + // The 256 byte length is to allow for the NUL terminator. + let mut temp_buffer: [c_char; 256] = [0; 256]; + + // SAFETY: should never be unsafe, as we're passing in a valid (0-initialized) buffer, and the + // length of said buffer. + unsafe { + cvt(c::gethostname(temp_buffer.as_mut_ptr() as _, temp_buffer.len() as _))?; + } + + // SAFETY: we already know the pointer here is valid, we made it from safe Rust earlier. + let cstring = unsafe { CStr::from_ptr(temp_buffer.as_mut_ptr()) }; + Ok(OsString::from(cstring.to_string_lossy().as_ref())) +} + //////////////////////////////////////////////////////////////////////////////// // TCP streams //////////////////////////////////////////////////////////////////////////////// From 62d9effbefbcbe7cca801d532bc2d292cc1c40a4 Mon Sep 17 00:00:00 2001 From: joboet Date: Fri, 24 Jan 2025 16:07:29 +0100 Subject: [PATCH 2/2] std: implement `hostname` using `GetHostNameW` on Windows, make UNIX code resilient against bad platform behaviour --- library/std/src/net/hostname.rs | 13 +++++++- library/std/src/sys/pal/unix/net.rs | 31 ++++++++++++++++++- .../std/src/sys/pal/windows/c/bindings.txt | 2 +- .../std/src/sys/pal/windows/c/windows_sys.rs | 2 +- library/std/src/sys/pal/windows/net.rs | 25 +++++++++++++-- library/std/src/sys_common/net.rs | 27 +--------------- 6 files changed, 68 insertions(+), 32 deletions(-) diff --git a/library/std/src/net/hostname.rs b/library/std/src/net/hostname.rs index 3e236086af752..ee920a560174f 100644 --- a/library/std/src/net/hostname.rs +++ b/library/std/src/net/hostname.rs @@ -5,7 +5,18 @@ use crate::ffi::OsString; /// 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 { - crate::sys_common::net::gethostname() + crate::sys::net::gethostname() } diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index d73b9fd5eb882..fab918e410514 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/pal/unix/net.rs @@ -1,8 +1,9 @@ use libc::{MSG_PEEK, c_int, c_void, size_t, sockaddr, socklen_t}; -use crate::ffi::CStr; +use crate::ffi::{CStr, OsString}; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; +use crate::os::unix::ffi::OsStringExt; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; use crate::sys::pal::unix::IsMinusOne; @@ -649,3 +650,31 @@ fn on_resolver_failure() { #[cfg(not(all(target_os = "linux", target_env = "gnu")))] fn on_resolver_failure() {} + +pub fn gethostname() -> io::Result { + // 255 bytes is the maximum allowable length for a hostname (as per the DNS spec), + // so we shouldn't ever have problems with this. I (@orowith2os) considered using a constant + // and letting the platform set the length, but it was determined after some discussion that + // this could break things if the platform changes their length. Possible alternative is to + // read the sysconf setting for the max hostname length, but that might be a bit too much work. + // The 256 byte length is to allow for the NUL terminator. + let mut temp_buffer = [0; 256]; + + // SAFETY: we're passing in a valid (0-initialized) buffer, and the length of said buffer. + unsafe { + cvt(libc::gethostname(temp_buffer.as_mut_ptr() as _, temp_buffer.len() as _))?; + } + + // Unfortunately, the UNIX specification says that the name will be truncated + // if it does not fit in the buffer, without returning. As additionally, the + // truncated name may still be null-terminated, there is no reliable way to + // detect truncation. Fortunately, most platforms ignore what the specication + // 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. + match CStr::from_bytes_until_nul(&temp_buffer) { + Ok(bytes) => Ok(OsString::from_vec(bytes.to_bytes().to_vec())), + Err(_) => Err(io::Error::from_raw_os_error(libc::ENAMETOOLONG)), + } +} diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index 3e9b2f10453ba..f40d4d485d794 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -1978,7 +1978,7 @@ Windows.Win32.Networking.WinSock.FD_SET Windows.Win32.Networking.WinSock.FIONBIO Windows.Win32.Networking.WinSock.freeaddrinfo Windows.Win32.Networking.WinSock.getaddrinfo -Windows.Win32.Networking.WinSock.gethostname +Windows.Win32.Networking.WinSock.GetHostNameW Windows.Win32.Networking.WinSock.getpeername Windows.Win32.Networking.WinSock.getsockname Windows.Win32.Networking.WinSock.getsockopt diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index bcf0b090ae28e..6f716cb14fcb2 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -113,6 +113,7 @@ windows_targets::link!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, e windows_targets::link!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); windows_targets::link!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32); windows_targets::link!("userenv.dll" "system" fn GetUserProfileDirectoryW(htoken : HANDLE, lpprofiledir : PWSTR, lpcchsize : *mut u32) -> BOOL); +windows_targets::link!("ws2_32.dll" "system" fn GetHostNameW(name : PWSTR, namelen : i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn WSACleanup() -> i32); windows_targets::link!("ws2_32.dll" "system" fn WSADuplicateSocketW(s : SOCKET, dwprocessid : u32, lpprotocolinfo : *mut WSAPROTOCOL_INFOW) -> i32); windows_targets::link!("ws2_32.dll" "system" fn WSAGetLastError() -> WSA_ERROR); @@ -126,7 +127,6 @@ windows_targets::link!("ws2_32.dll" "system" fn closesocket(s : SOCKET) -> i32); windows_targets::link!("ws2_32.dll" "system" fn connect(s : SOCKET, name : *const SOCKADDR, namelen : i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn freeaddrinfo(paddrinfo : *const ADDRINFOA)); windows_targets::link!("ws2_32.dll" "system" fn getaddrinfo(pnodename : PCSTR, pservicename : PCSTR, phints : *const ADDRINFOA, ppresult : *mut *mut ADDRINFOA) -> i32); -windows_targets::link!("ws2_32.dll" "system" fn gethostname(name : PSTR, namelen : i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn getpeername(s : SOCKET, name : *mut SOCKADDR, namelen : *mut i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn getsockname(s : SOCKET, name : *mut SOCKADDR, namelen : *mut i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn getsockopt(s : SOCKET, level : i32, optname : i32, optval : PSTR, optlen : *mut i32) -> i32); diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs index 8e2ea4fe5d3ac..68f7f532368be 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/pal/windows/net.rs @@ -2,8 +2,10 @@ use core::ffi::{c_int, c_long, c_ulong, c_ushort}; +use crate::ffi::OsString; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut, Read}; use crate::net::{Shutdown, SocketAddr}; +use crate::os::windows::ffi::OsStringExt; use crate::os::windows::io::{ AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, }; @@ -31,8 +33,8 @@ pub mod netc { IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, IPPROTO_IP, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, SO_BROADCAST, SO_RCVTIMEO, SO_SNDTIMEO, SOCK_DGRAM, SOCK_STREAM, SOCKADDR as sockaddr, - SOCKADDR_STORAGE as sockaddr_storage, SOL_SOCKET, bind, connect, freeaddrinfo, gethostname, - getpeername, getsockname, getsockopt, listen, setsockopt, + SOCKADDR_STORAGE as sockaddr_storage, SOL_SOCKET, bind, connect, freeaddrinfo, getpeername, + getsockname, getsockopt, listen, setsockopt, }; #[allow(non_camel_case_types)] @@ -572,3 +574,22 @@ impl FromRawSocket for Socket { unsafe { Self(FromRawSocket::from_raw_socket(raw_socket)) } } } + +pub fn gethostname() -> io::Result { + init(); + + // The documentation of GetHostNameW says that a buffer size of 256 is + // always enough. + let mut buffer = [0; 256]; + // SAFETY: these parameters specify a valid, writable region of memory. + let ret = unsafe { c::GetHostNameW(buffer.as_mut_ptr(), buffer.len() as i32) }; + if ret == 0 { + let len = buffer + .iter() + .position(|&w| w == 0) + .expect("GetHostNameW failed to return a null-terminated name"); + Ok(OsString::from_wide(&buffer[..len])) + } else { + Err(io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() })) + } +} diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 36dc3cf63bd26..74306978d2284 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests; -use crate::ffi::{CStr, OsString, c_char, c_int, c_void}; +use crate::ffi::{c_int, c_void}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::sys::common::small_c_string::run_with_cstr; @@ -215,31 +215,6 @@ impl<'a> TryFrom<(&'a str, u16)> for LookupHost { } } -//////////////////////////////////////////////////////////////////////////////// -// gethostname -//////////////////////////////////////////////////////////////////////////////// - -pub fn gethostname() -> crate::io::Result { - init(); - // 255 bytes is the maximum allowable length for a hostname (as per the DNS spec), - // so we shouldn't ever have problems with this. I (@orowith2os) considered using a constant - // and letting the platform set the length, but it was determined after some discussion that - // this could break things if the platform changes their length. Possible alternative is to - // read the sysconf setting for the max hostname length, but that might be a bit too much work. - // The 256 byte length is to allow for the NUL terminator. - let mut temp_buffer: [c_char; 256] = [0; 256]; - - // SAFETY: should never be unsafe, as we're passing in a valid (0-initialized) buffer, and the - // length of said buffer. - unsafe { - cvt(c::gethostname(temp_buffer.as_mut_ptr() as _, temp_buffer.len() as _))?; - } - - // SAFETY: we already know the pointer here is valid, we made it from safe Rust earlier. - let cstring = unsafe { CStr::from_ptr(temp_buffer.as_mut_ptr()) }; - Ok(OsString::from(cstring.to_string_lossy().as_ref())) -} - //////////////////////////////////////////////////////////////////////////////// // TCP streams ////////////////////////////////////////////////////////////////////////////////