-
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3e8355b
commit 232ddc1
Showing
4 changed files
with
149 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ features = [ | |
"fileapi", | ||
"minwindef", | ||
"processenv", | ||
"sysinfoapi", | ||
"winbase", | ||
"wincon", | ||
"winerror", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
use std::{ffi::OsString, io}; | ||
|
||
use winapi::um::sysinfoapi::{GetComputerNameExW, COMPUTER_NAME_FORMAT}; | ||
|
||
/// The type of name to be retrieved by [`get_computer_name`]. | ||
#[derive(Clone, Copy, Debug)] | ||
#[non_exhaustive] | ||
pub enum ComputerNameKind { | ||
/// The name of the DNS domain assigned to the local computer. If the local | ||
/// computer is a node in a cluster, lpBuffer receives the DNS domain name | ||
/// of the cluster virtual server. | ||
DnsDomain, | ||
/// The fully qualified DNS name that uniquely identifies the local | ||
/// computer. This name is a combination of the DNS host name and the DNS | ||
/// domain name, using the form HostName.DomainName. If the local computer | ||
/// is a node in a cluster, lpBuffer receives the fully qualified DNS name | ||
/// of the cluster virtual server. | ||
DnsFullyQualified, | ||
/// The DNS host name of the local computer. If the local computer is a | ||
/// node in a cluster, lpBuffer receives the DNS host name of the cluster | ||
/// virtual server. | ||
DnsHostname, | ||
/// The NetBIOS name of the local computer. If the local computer is a node | ||
/// in a cluster, lpBuffer receives the NetBIOS name of the cluster virtual | ||
/// server. | ||
NetBios, | ||
/// The name of the DNS domain assigned to the local computer. If the local | ||
/// computer is a node in a cluster, lpBuffer receives the DNS domain name | ||
/// of the local computer, not the name of the cluster virtual server. | ||
PhysicalDnsDomain, | ||
/// The fully qualified DNS name that uniquely identifies the computer. If | ||
/// the local computer is a node in a cluster, lpBuffer receives the fully | ||
/// qualified DNS name of the local computer, not the name of the cluster | ||
/// virtual server. | ||
/// | ||
/// The fully qualified DNS name is a combination of the DNS host name and | ||
/// the DNS domain name, using the form HostName.DomainName. | ||
PhysicalDnsFullyQualified, | ||
/// The DNS host name of the local computer. If the local computer is a | ||
/// node in a cluster, lpBuffer receives the DNS host name of the local | ||
/// computer, not the name of the cluster virtual server. | ||
PhysicalDnsHostname, | ||
/// The NetBIOS name of the local computer. If the local computer is a node | ||
/// in a cluster, lpBuffer receives the NetBIOS name of the local computer, | ||
/// not the name of the cluster virtual server. | ||
PhysicalNetBios, | ||
} | ||
|
||
impl ComputerNameKind { | ||
fn to_format(&self) -> COMPUTER_NAME_FORMAT { | ||
use self::ComputerNameKind::*; | ||
use winapi::um::sysinfoapi; | ||
|
||
match *self { | ||
DnsDomain => sysinfoapi::ComputerNameDnsDomain, | ||
DnsFullyQualified => sysinfoapi::ComputerNameDnsFullyQualified, | ||
DnsHostname => sysinfoapi::ComputerNameDnsHostname, | ||
NetBios => sysinfoapi::ComputerNameNetBIOS, | ||
PhysicalDnsDomain => sysinfoapi::ComputerNamePhysicalDnsDomain, | ||
PhysicalDnsFullyQualified => { | ||
sysinfoapi::ComputerNamePhysicalDnsFullyQualified | ||
} | ||
PhysicalDnsHostname => sysinfoapi::ComputerNamePhysicalDnsHostname, | ||
PhysicalNetBios => sysinfoapi::ComputerNamePhysicalNetBIOS, | ||
} | ||
} | ||
} | ||
/// Retrieves a NetBIOS or DNS name associated with the local computer. | ||
/// | ||
/// The names are established at system startup, when the system reads them | ||
/// from the registry. | ||
/// | ||
/// This corresponds to calling [`GetComputerNameExW`]. | ||
/// | ||
/// [`GetComputerNameExW`]: https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getcomputernameexw | ||
pub fn get_computer_name(kind: ComputerNameKind) -> io::Result<OsString> { | ||
use std::os::windows::ffi::OsStringExt; | ||
|
||
let format = kind.to_format(); | ||
let mut len1 = 0; | ||
// SAFETY: As documented, we call this with a null pointer which will in | ||
// turn cause this routine to write the required buffer size fo `len1`. | ||
// Also, we explicitly ignore the return value since we expect this call to | ||
// fail given that the destination buffer is too small by design. | ||
let _ = | ||
unsafe { GetComputerNameExW(format, std::ptr::null_mut(), &mut len1) }; | ||
|
||
let len = match usize::try_from(len1) { | ||
Ok(len) => len, | ||
Err(_) => { | ||
return Err(io::Error::new( | ||
io::ErrorKind::Other, | ||
"GetComputerNameExW buffer length overflowed usize", | ||
)) | ||
} | ||
}; | ||
let mut buf = vec![0; len]; | ||
let mut len2 = len1; | ||
// SAFETY: We pass a valid pointer to an appropriately sized Vec<u16>. | ||
let rc = | ||
unsafe { GetComputerNameExW(format, buf.as_mut_ptr(), &mut len2) }; | ||
if rc == 0 { | ||
return Err(io::Error::last_os_error()); | ||
} | ||
// Apparently, the subsequent call write the number of characters written | ||
// to the buffer to `len2` but not including the NUL terminator. Notice | ||
// that in the first call above, the length written to `len1` *does* | ||
// include the NUL terminator. Therefore, we expect `len1` to be one more | ||
// than `len2`. If not, then something weird has happened and we report an | ||
// error. | ||
if len1 != len2.saturating_add(1) { | ||
return Err(io::Error::new( | ||
io::ErrorKind::Other, | ||
"GetComputerNameExW buffer length mismatch", | ||
)); | ||
} | ||
let len = usize::try_from(len2).expect("len1 fits implies len2 fits"); | ||
Ok(OsString::from_wide(&buf[..len])) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn itworks() { | ||
let kinds = [ | ||
ComputerNameKind::DnsDomain, | ||
ComputerNameKind::DnsFullyQualified, | ||
ComputerNameKind::DnsHostname, | ||
ComputerNameKind::NetBios, | ||
ComputerNameKind::PhysicalDnsDomain, | ||
ComputerNameKind::PhysicalDnsFullyQualified, | ||
ComputerNameKind::PhysicalDnsHostname, | ||
ComputerNameKind::PhysicalNetBios, | ||
]; | ||
for kind in kinds { | ||
let result = get_computer_name(kind); | ||
let name = result.unwrap(); | ||
println!("{kind:?}: {name:?}"); | ||
} | ||
} | ||
} |