Skip to content

Commit

Permalink
progress
Browse files Browse the repository at this point in the history
  • Loading branch information
BurntSushi committed Sep 20, 2023
1 parent 3e8355b commit 232ddc1
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ jobs:
- run: cargo build --verbose
- run: cargo doc --verbose
- run: cargo test --verbose
- name: Show all computer names
run: cargo test --lib sysinfo::tests::itworks -- --nocapture

rustfmt:
name: rustfmt
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ features = [
"fileapi",
"minwindef",
"processenv",
"sysinfoapi",
"winbase",
"wincon",
"winerror",
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ pub mod console;
#[cfg(windows)]
pub mod file;
#[cfg(windows)]
/// Safe routines for querying various Windows specific properties.
pub mod sysinfo;
#[cfg(windows)]
mod win;
143 changes: 143 additions & 0 deletions src/sysinfo.rs
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:?}");
}
}
}

0 comments on commit 232ddc1

Please sign in to comment.