diff options
| author | joboet <jonasboettiger@icloud.com> | 2025-09-23 17:14:51 +0200 |
|---|---|---|
| committer | joboet <jonasboettiger@icloud.com> | 2025-09-29 10:24:54 +0200 |
| commit | 97333f8c9a0f774cc8d0025bbc51848e1f60427d (patch) | |
| tree | 8fe2a48d16ac84930947e2055aafdb27240847bc /library/std/src/sys/net/hostname/unix.rs | |
| parent | af1b14bb9b1b3d301c1e8e0a989d814deaf054c9 (diff) | |
| download | rust-97333f8c9a0f774cc8d0025bbc51848e1f60427d.tar.gz rust-97333f8c9a0f774cc8d0025bbc51848e1f60427d.zip | |
std: implement `hostname`
Diffstat (limited to 'library/std/src/sys/net/hostname/unix.rs')
| -rw-r--r-- | library/std/src/sys/net/hostname/unix.rs | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/library/std/src/sys/net/hostname/unix.rs b/library/std/src/sys/net/hostname/unix.rs new file mode 100644 index 00000000000..bc6fa82a38f --- /dev/null +++ b/library/std/src/sys/net/hostname/unix.rs @@ -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)?; + } +} |
