Coverage Report

Created: 2026-11-12 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/hostname-0.3.1/src/nix.rs
Line
Count
Source
1
use std::io;
2
#[cfg(feature = "set")]
3
use std::ffi::OsStr;
4
use std::ffi::OsString;
5
#[cfg(feature = "set")]
6
use std::os::unix::ffi::OsStrExt;
7
use std::os::unix::ffi::OsStringExt;
8
9
use libc;
10
11
0
pub fn get() -> io::Result<OsString> {
12
    // According to the POSIX specification,
13
    // host names are limited to `HOST_NAME_MAX` bytes
14
    //
15
    // https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html
16
0
    let size =
17
0
        unsafe { libc::sysconf(libc::_SC_HOST_NAME_MAX) as libc::size_t };
18
19
0
    let mut buffer = vec![0u8; size];
20
21
0
    let result = unsafe {
22
0
        libc::gethostname(buffer.as_mut_ptr() as *mut libc::c_char, size)
23
    };
24
25
0
    if result != 0 {
26
0
        return Err(io::Error::last_os_error());
27
0
    }
28
29
0
    Ok(wrap_buffer(buffer))
30
0
}
31
32
0
fn wrap_buffer(mut bytes: Vec<u8>) -> OsString {
33
    // Returned name might be truncated if it does not fit
34
    // and `buffer` will not contain the trailing \0 in that case.
35
    // Manually capping the buffer length here.
36
0
    let end = bytes
37
0
        .iter()
38
0
        .position(|&byte| byte == 0x00)
39
0
        .unwrap_or_else(|| bytes.len());
40
0
    bytes.resize(end, 0x00);
41
42
0
    OsString::from_vec(bytes)
43
0
}
44
45
#[cfg(feature = "set")]
46
pub fn set(hostname: &OsStr) -> io::Result<()> {
47
    #[cfg(not(any(target_os = "dragonfly",
48
                     target_os = "freebsd",
49
                     target_os = "ios",
50
                     target_os = "macos")))]
51
    #[allow(non_camel_case_types)]
52
    type hostname_len_t = libc::size_t;
53
54
    #[cfg(any(target_os = "dragonfly",
55
                     target_os = "freebsd",
56
                     target_os = "ios",
57
                     target_os = "macos"))]
58
    #[allow(non_camel_case_types)]
59
    type hostname_len_t = libc::c_int;
60
61
    let size = hostname.len() as hostname_len_t;
62
63
    let result = unsafe {
64
        libc::sethostname(
65
            hostname.as_bytes().as_ptr() as *const libc::c_char,
66
            size,
67
        )
68
    };
69
70
    if result != 0 {
71
        Err(io::Error::last_os_error())
72
    } else {
73
        Ok(())
74
    }
75
}
76
77
#[cfg(test)]
78
mod tests {
79
    use std::ffi::OsStr;
80
81
    use super::wrap_buffer;
82
83
    // Happy path case: there is a correct null terminated C string in a buffer
84
    // and a bunch of NULL characters from the pre-allocated buffer
85
    #[test]
86
    fn test_non_overflowed_buffer() {
87
        let buf = b"potato\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0".to_vec();
88
89
        assert_eq!(wrap_buffer(buf), OsStr::new("potato"));
90
    }
91
92
    #[test]
93
    fn test_empty_buffer() {
94
        let buf = b"".to_vec();
95
96
        assert_eq!(wrap_buffer(buf), OsStr::new(""));
97
    }
98
99
    #[test]
100
    fn test_filled_with_null_buffer() {
101
        let buf = b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0".to_vec();
102
103
        assert_eq!(wrap_buffer(buf), OsStr::new(""));
104
    }
105
106
    // Hostname value had overflowed the buffer, so it was truncated
107
    // and according to the POSIX documentation of the `gethostname`:
108
    //
109
    // > it is unspecified whether the returned name is null-terminated.
110
    #[test]
111
    fn test_overflowed_buffer() {
112
        let buf = b"potat".to_vec();
113
114
        assert_eq!(wrap_buffer(buf), OsStr::new("potat"));
115
    }
116
}