From 6d9b4000cc7c493ae7b4bdf6f415eba7d2e51986 Mon Sep 17 00:00:00 2001
From: Dallas Strouse <oro@CrystalBox.kirara>
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<OsString> {
+    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<OsString> {
+    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<OsString> {
+    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 <jonasboettiger@icloud.com>
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<OsString> {
-    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<OsString> {
+    // 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<OsString> {
+    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<OsString> {
-    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
 ////////////////////////////////////////////////////////////////////////////////