1 use std::{ffi::OsString, io};
2
3 use winapi::um::sysinfoapi::{GetComputerNameExW, COMPUTER_NAME_FORMAT};
4
5 /// The type of name to be retrieved by [`get_computer_name`].
6 #[derive(Clone, Copy, Debug)]
7 #[non_exhaustive]
8 pub enum ComputerNameKind {
9 /// The name of the DNS domain assigned to the local computer. If the local
10 /// computer is a node in a cluster, lpBuffer receives the DNS domain name
11 /// of the cluster virtual server.
12 DnsDomain,
13 /// The fully qualified DNS name that uniquely identifies the local
14 /// computer. This name is a combination of the DNS host name and the DNS
15 /// domain name, using the form HostName.DomainName. If the local computer
16 /// is a node in a cluster, lpBuffer receives the fully qualified DNS name
17 /// of the cluster virtual server.
18 DnsFullyQualified,
19 /// The DNS host name of the local computer. If the local computer is a
20 /// node in a cluster, lpBuffer receives the DNS host name of the cluster
21 /// virtual server.
22 DnsHostname,
23 /// The NetBIOS name of the local computer. If the local computer is a node
24 /// in a cluster, lpBuffer receives the NetBIOS name of the cluster virtual
25 /// server.
26 NetBios,
27 /// The name of the DNS domain assigned to the local computer. If the local
28 /// computer is a node in a cluster, lpBuffer receives the DNS domain name
29 /// of the local computer, not the name of the cluster virtual server.
30 PhysicalDnsDomain,
31 /// The fully qualified DNS name that uniquely identifies the computer. If
32 /// the local computer is a node in a cluster, lpBuffer receives the fully
33 /// qualified DNS name of the local computer, not the name of the cluster
34 /// virtual server.
35 ///
36 /// The fully qualified DNS name is a combination of the DNS host name and
37 /// the DNS domain name, using the form HostName.DomainName.
38 PhysicalDnsFullyQualified,
39 /// The DNS host name of the local computer. If the local computer is a
40 /// node in a cluster, lpBuffer receives the DNS host name of the local
41 /// computer, not the name of the cluster virtual server.
42 PhysicalDnsHostname,
43 /// The NetBIOS name of the local computer. If the local computer is a node
44 /// in a cluster, lpBuffer receives the NetBIOS name of the local computer,
45 /// not the name of the cluster virtual server.
46 PhysicalNetBios,
47 }
48
49 impl ComputerNameKind {
to_format(&self) -> COMPUTER_NAME_FORMAT50 fn to_format(&self) -> COMPUTER_NAME_FORMAT {
51 use self::ComputerNameKind::*;
52 use winapi::um::sysinfoapi;
53
54 match *self {
55 DnsDomain => sysinfoapi::ComputerNameDnsDomain,
56 DnsFullyQualified => sysinfoapi::ComputerNameDnsFullyQualified,
57 DnsHostname => sysinfoapi::ComputerNameDnsHostname,
58 NetBios => sysinfoapi::ComputerNameNetBIOS,
59 PhysicalDnsDomain => sysinfoapi::ComputerNamePhysicalDnsDomain,
60 PhysicalDnsFullyQualified => {
61 sysinfoapi::ComputerNamePhysicalDnsFullyQualified
62 }
63 PhysicalDnsHostname => sysinfoapi::ComputerNamePhysicalDnsHostname,
64 PhysicalNetBios => sysinfoapi::ComputerNamePhysicalNetBIOS,
65 }
66 }
67 }
68 /// Retrieves a NetBIOS or DNS name associated with the local computer.
69 ///
70 /// The names are established at system startup, when the system reads them
71 /// from the registry.
72 ///
73 /// This corresponds to calling [`GetComputerNameExW`].
74 ///
75 /// [`GetComputerNameExW`]: https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getcomputernameexw
get_computer_name(kind: ComputerNameKind) -> io::Result<OsString>76 pub fn get_computer_name(kind: ComputerNameKind) -> io::Result<OsString> {
77 use std::os::windows::ffi::OsStringExt;
78
79 let format = kind.to_format();
80 let mut len1 = 0;
81 // SAFETY: As documented, we call this with a null pointer which will in
82 // turn cause this routine to write the required buffer size fo `len1`.
83 // Also, we explicitly ignore the return value since we expect this call to
84 // fail given that the destination buffer is too small by design.
85 let _ =
86 unsafe { GetComputerNameExW(format, std::ptr::null_mut(), &mut len1) };
87
88 let len = match usize::try_from(len1) {
89 Ok(len) => len,
90 Err(_) => {
91 return Err(io::Error::new(
92 io::ErrorKind::Other,
93 "GetComputerNameExW buffer length overflowed usize",
94 ))
95 }
96 };
97 let mut buf = vec![0; len];
98 let mut len2 = len1;
99 // SAFETY: We pass a valid pointer to an appropriately sized Vec<u16>.
100 let rc =
101 unsafe { GetComputerNameExW(format, buf.as_mut_ptr(), &mut len2) };
102 if rc == 0 {
103 return Err(io::Error::last_os_error());
104 }
105 // Apparently, the subsequent call writes the number of characters written
106 // to the buffer to `len2` but not including the NUL terminator. Notice
107 // that in the first call above, the length written to `len1` *does*
108 // include the NUL terminator. Therefore, we expect `len1` to be at least
109 // one greater than `len2`. If not, then something weird has happened and
110 // we report an error.
111 if len1 <= len2 {
112 let msg = format!(
113 "GetComputerNameExW buffer length mismatch, \
114 expected length strictly less than {} \
115 but got {}",
116 len1, len2,
117 );
118 return Err(io::Error::new(io::ErrorKind::Other, msg));
119 }
120 let len = usize::try_from(len2).expect("len1 fits implies len2 fits");
121 Ok(OsString::from_wide(&buf[..len]))
122 }
123
124 #[cfg(test)]
125 mod tests {
126 use super::*;
127
128 // This test doesn't really check anything other than that we can
129 // successfully query all kinds of computer names. We just print them out
130 // since there aren't really any properties about the names that we can
131 // assert.
132 //
133 // We specifically run this test in CI with --nocapture so that we can see
134 // the output.
135 #[test]
itworks()136 fn itworks() {
137 let kinds = [
138 ComputerNameKind::DnsDomain,
139 ComputerNameKind::DnsFullyQualified,
140 ComputerNameKind::DnsHostname,
141 ComputerNameKind::NetBios,
142 ComputerNameKind::PhysicalDnsDomain,
143 ComputerNameKind::PhysicalDnsFullyQualified,
144 ComputerNameKind::PhysicalDnsHostname,
145 ComputerNameKind::PhysicalNetBios,
146 ];
147 for kind in kinds {
148 let result = get_computer_name(kind);
149 let name = result.unwrap();
150 println!("{kind:?}: {name:?}");
151 }
152 }
153 }
154