Skip to content

Commit 97333f8

Browse files
committed
std: implement hostname
1 parent af1b14b commit 97333f8

File tree

9 files changed

+140
-1
lines changed

9 files changed

+140
-1
lines changed

library/std/src/net/hostname.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use crate::ffi::OsString;
2+
3+
/// Returns the system hostname.
4+
///
5+
/// This can error out in platform-specific error cases;
6+
/// for example, uefi and wasm, where hostnames aren't
7+
/// supported.
8+
///
9+
/// # Underlying system calls
10+
///
11+
/// | Platform | System call |
12+
/// |----------|---------------------------------------------------------------------------------------------------------|
13+
/// | UNIX | [`gethostname`](https://www.man7.org/linux/man-pages/man2/gethostname.2.html) |
14+
/// | Windows | [`GetHostNameW`](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-gethostnamew) |
15+
///
16+
/// Note that platform-specific behavior [may change in the future][changes].
17+
///
18+
/// [changes]: crate::io#platform-specific-behavior
19+
#[unstable(feature = "gethostname", issue = "135142")]
20+
pub fn hostname() -> crate::io::Result<OsString> {
21+
crate::sys::net::hostname()
22+
}

library/std/src/net/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! Networking primitives for TCP/UDP communication.
22
//!
33
//! This module provides networking functionality for the Transmission Control and User
4-
//! Datagram Protocols, as well as types for IP and socket addresses.
4+
//! Datagram Protocols, as well as types for IP and socket addresses and functions related
5+
//! to network properties.
56
//!
67
//! # Organization
78
//!
@@ -24,6 +25,8 @@
2425
#[stable(feature = "rust1", since = "1.0.0")]
2526
pub use core::net::AddrParseError;
2627

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

41+
mod hostname;
3842
mod ip_addr;
3943
mod socket_addr;
4044
mod tcp;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
cfg_select! {
2+
target_family = "unix" => {
3+
mod unix;
4+
pub use unix::hostname;
5+
}
6+
target_os = "windows" => {
7+
mod windows;
8+
pub use windows::hostname;
9+
}
10+
_ => {
11+
mod unsupported;
12+
pub use unsupported::hostname;
13+
}
14+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use crate::ffi::OsString;
2+
use crate::io;
3+
use crate::os::unix::ffi::OsStringExt;
4+
use crate::sys::pal::os::errno;
5+
6+
pub fn hostname() -> io::Result<OsString> {
7+
// Query the system for the maximum host name length.
8+
let host_name_max = match unsafe { libc::sysconf(libc::_SC_HOST_NAME_MAX) } {
9+
// If this fails (possibly because there is no maximum length), then
10+
// assume a maximum length of _POSIX_HOST_NAME_MAX (255).
11+
-1 => 255,
12+
max => max as usize,
13+
};
14+
15+
// Reserve space for the nul terminator too.
16+
let mut buf = Vec::<u8>::try_with_capacity(host_name_max + 1)?;
17+
loop {
18+
// SAFETY: `buf.capacity()` bytes of `buf` are writable.
19+
let r = unsafe { libc::gethostname(buf.as_mut_ptr().cast(), buf.capacity()) };
20+
match (r != 0).then(errno) {
21+
None => {
22+
// Unfortunately, the UNIX specification says that the name will
23+
// be truncated if it does not fit in the buffer, without returning
24+
// an error. As additionally, the truncated name may still be null-
25+
// terminated, there is no reliable way to detect truncation.
26+
// Fortunately, most platforms ignore what the specification says
27+
// and return an error (mostly ENAMETOOLONG). Should that not be
28+
// the case, the following detects truncation if the null-terminator
29+
// was omitted. Note that this check does not impact performance at
30+
// all as we need to find the length of the string anyways.
31+
//
32+
// Use `strnlen` as it does not place an initialization requirement
33+
// on the bytes after the nul terminator.
34+
//
35+
// SAFETY: `buf.capacity()` bytes of `buf` are accessible, and are
36+
// initialized up to and including a possible nul terminator.
37+
let len = unsafe { libc::strnlen(buf.as_ptr().cast(), buf.capacity()) };
38+
if len < buf.capacity() {
39+
// If the string is nul-terminated, we assume that is has not
40+
// been truncated, as the capacity *should be* enough to hold
41+
// `HOST_NAME_MAX` bytes.
42+
// SAFETY: `len + 1` bytes have been initialized (we exclude
43+
// the nul terminator from the string).
44+
unsafe { buf.set_len(len) };
45+
return Ok(OsString::from_vec(buf));
46+
}
47+
}
48+
// As `buf.capacity()` is always less than or equal to `isize::MAX`
49+
// (Rust allocations cannot exceed that limit), the only way `EINVAL`
50+
// can be returned is if the system uses `EINVAL` to report that the
51+
// name does not fit in the provided buffer. In that case (or in the
52+
// case of `ENAMETOOLONG`), resize the buffer and try again.
53+
Some(libc::EINVAL | libc::ENAMETOOLONG) => {}
54+
// Other error codes (e.g. EPERM) have nothing to do with the buffer
55+
// size and should be returned to the user.
56+
Some(err) => return Err(io::Error::from_raw_os_error(err)),
57+
}
58+
59+
// Resize the buffer (according to `Vec`'s resizing rules) and try again.
60+
buf.try_reserve(buf.capacity() + 1)?;
61+
}
62+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use crate::ffi::OsString;
2+
use crate::io::{Error, Result};
3+
4+
pub fn hostname() -> Result<OsString> {
5+
Err(Error::UNSUPPORTED_PLATFORM)
6+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use crate::ffi::OsString;
2+
use crate::io::Result;
3+
use crate::mem::MaybeUninit;
4+
use crate::os::windows::ffi::OsStringExt;
5+
use crate::sys::pal::c;
6+
use crate::sys::pal::winsock::{self, cvt};
7+
8+
pub fn hostname() -> Result<OsString> {
9+
winsock::startup();
10+
11+
// The documentation of GetHostNameW says that a buffer size of 256 is
12+
// always enough.
13+
let mut buffer = [const { MaybeUninit::<u16>::uninit() }; 256];
14+
// SAFETY: these parameters specify a valid, writable region of memory.
15+
cvt(unsafe { c::GetHostNameW(buffer.as_mut_ptr().cast(), buffer.len() as i32) })?;
16+
// Use `lstrlenW` here as it does not require the bytes after the nul
17+
// terminator to be initialized.
18+
// SAFETY: if `GetHostNameW` returns successfully, the name is nul-terminated.
19+
let len = unsafe { c::lstrlenW(buffer.as_ptr().cast()) };
20+
// SAFETY: the length of the name is `len`, hence `len` bytes have been
21+
// initialized by `GetHostNameW`.
22+
let name = unsafe { buffer[..len as usize].assume_init_ref() };
23+
Ok(OsString::from_wide(name))
24+
}

library/std/src/sys/net/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@
22
/// `UdpSocket` as well as related functionality like DNS resolving.
33
mod connection;
44
pub use connection::*;
5+
6+
mod hostname;
7+
pub use hostname::hostname;

library/std/src/sys/pal/windows/c/bindings.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2170,6 +2170,7 @@ GetFileType
21702170
GETFINALPATHNAMEBYHANDLE_FLAGS
21712171
GetFinalPathNameByHandleW
21722172
GetFullPathNameW
2173+
GetHostNameW
21732174
GetLastError
21742175
GetModuleFileNameW
21752176
GetModuleHandleA
@@ -2270,6 +2271,7 @@ LPPROGRESS_ROUTINE
22702271
LPPROGRESS_ROUTINE_CALLBACK_REASON
22712272
LPTHREAD_START_ROUTINE
22722273
LPWSAOVERLAPPED_COMPLETION_ROUTINE
2274+
lstrlenW
22732275
M128A
22742276
MAX_PATH
22752277
MAXIMUM_REPARSE_DATA_BUFFER_SIZE

library/std/src/sys/pal/windows/c/windows_sys.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetFileSizeEx(hfile : HANDLE,
4949
windows_targets::link!("kernel32.dll" "system" fn GetFileType(hfile : HANDLE) -> FILE_TYPE);
5050
windows_targets::link!("kernel32.dll" "system" fn GetFinalPathNameByHandleW(hfile : HANDLE, lpszfilepath : PWSTR, cchfilepath : u32, dwflags : GETFINALPATHNAMEBYHANDLE_FLAGS) -> u32);
5151
windows_targets::link!("kernel32.dll" "system" fn GetFullPathNameW(lpfilename : PCWSTR, nbufferlength : u32, lpbuffer : PWSTR, lpfilepart : *mut PWSTR) -> u32);
52+
windows_targets::link!("ws2_32.dll" "system" fn GetHostNameW(name : PWSTR, namelen : i32) -> i32);
5253
windows_targets::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR);
5354
windows_targets::link!("kernel32.dll" "system" fn GetModuleFileNameW(hmodule : HMODULE, lpfilename : PWSTR, nsize : u32) -> u32);
5455
windows_targets::link!("kernel32.dll" "system" fn GetModuleHandleA(lpmodulename : PCSTR) -> HMODULE);
@@ -134,6 +135,7 @@ windows_targets::link!("ws2_32.dll" "system" fn getsockname(s : SOCKET, name : *
134135
windows_targets::link!("ws2_32.dll" "system" fn getsockopt(s : SOCKET, level : i32, optname : i32, optval : PSTR, optlen : *mut i32) -> i32);
135136
windows_targets::link!("ws2_32.dll" "system" fn ioctlsocket(s : SOCKET, cmd : i32, argp : *mut u32) -> i32);
136137
windows_targets::link!("ws2_32.dll" "system" fn listen(s : SOCKET, backlog : i32) -> i32);
138+
windows_targets::link!("kernel32.dll" "system" fn lstrlenW(lpstring : PCWSTR) -> i32);
137139
windows_targets::link!("ws2_32.dll" "system" fn recv(s : SOCKET, buf : PSTR, len : i32, flags : SEND_RECV_FLAGS) -> i32);
138140
windows_targets::link!("ws2_32.dll" "system" fn recvfrom(s : SOCKET, buf : PSTR, len : i32, flags : i32, from : *mut SOCKADDR, fromlen : *mut i32) -> i32);
139141
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);

0 commit comments

Comments
 (0)