diff --git a/library/std/src/net/hostname.rs b/library/std/src/net/hostname.rs new file mode 100644 index 0000000000000..ee920a560174f --- /dev/null +++ b/library/std/src/net/hostname.rs @@ -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 { + crate::sys::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/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/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..f40d4d485d794 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.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 79513d33a1ac7..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); diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs index a92853c642c06..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, }; @@ -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() })) + } +}