diff --git a/.travis.yml b/.travis.yml index d5a27b7..3e5736e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,9 @@ jobs: - stage: test os: osx rust: stable + - stage: test + os: windows + rust: stable - stage: test os: linux @@ -33,3 +36,8 @@ jobs: rust: stable before_script: rustup component add clippy script: cargo clippy --all-targets + - stage: lint + os: windows + rust: stable + before_script: rustup component add clippy + script: cargo clippy --all-targets diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fb8341..22aa951 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Add Windows implementation (see [GH-1]). + +[Gh-1]: https://github.com/lunaryorn/gethostname.rs/pull/1 + ## 0.1.0 – 2019-01-20 Initial release. diff --git a/Cargo.toml b/Cargo.toml index 63bda13..6bdc50d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,6 @@ pretty_assertions = "^0.5" [target.'cfg(not(windows))'.dependencies] libc = "^0.2" + +[target.'cfg(windows)'.dependencies] +winapi = {version = "^0.3", features = ["sysinfoapi"]} diff --git a/src/lib.rs b/src/lib.rs index ec874bd..f44e736 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,10 +23,10 @@ use std::io::Error; /// Get the standard host name for the current machine. /// -/// Wraps POSIX [gethostname] in a safe interface. The function doesn’t fail but -/// it may `panic!` if the internal buffer for the hostname is too small, but we -/// use a buffer large enough to hold the maximum hostname, so we consider any -/// panics from this function as bug which you should report. +/// Wraps POSIX [gethostname] in a safe interface. The function may `panic!` if +/// the internal buffer for the hostname is too small, but we use a buffer large +/// enough to hold the maximum hostname, so we consider any panics from this +/// function as bug which you should report. /// /// [gethostname]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html #[cfg(not(windows))] @@ -59,13 +59,63 @@ pub fn gethostname() -> OsString { OsString::from_vec(buffer) } +/// Get the standard hostname for the current machine. +/// +/// Returns the DNS host name of the local computer, as returned by +/// [GetComputerNameExW] with `ComputerNamePhysicalDnsHostname` flag has name +/// type. This function may `panic!` if the internal buffer for the hostname is +/// too small. Since we try to allocate a buffer large enough to hold the host +/// name we consider panics a bug which you should report. +/// +/// [GetComputerNameExW]: https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getcomputernameexw +#[cfg(windows)] +pub fn gethostname() -> OsString { + use std::os::windows::ffi::OsStringExt; + use winapi::ctypes::{c_ulong, wchar_t}; + use winapi::um::sysinfoapi::{ComputerNamePhysicalDnsHostname, GetComputerNameExW}; + + let mut buffer_size: c_ulong = 0; + + unsafe { + // This call always fails with ERROR_MORE_DATA, because we pass NULL to + // get the required buffer size. + GetComputerNameExW( + ComputerNamePhysicalDnsHostname, + std::ptr::null_mut(), + &mut buffer_size, + ) + }; + + let mut buffer = vec![0 as wchar_t; buffer_size as usize]; + let returncode = unsafe { + GetComputerNameExW( + ComputerNamePhysicalDnsHostname, + buffer.as_mut_ptr() as *mut wchar_t, + &mut buffer_size, + ) + }; + // GetComputerNameExW returns a non-zero value on success! + if returncode == 0 { + panic!( + "GetComputerNameExW failed to read hostname: {} +Please report this issue to !", + Error::last_os_error() + ); + } + + let end = buffer + .iter() + .position(|&b| b == 0) + .unwrap_or_else(|| buffer.len()); + OsString::from_wide(&buffer[0..end]) +} + #[cfg(test)] mod tests { use pretty_assertions::assert_eq; use std::process::Command; #[test] - #[cfg(not(windows))] fn gethostname_matches_system_hostname() { let output = Command::new("hostname") .output()