/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 | | } |