who: fix --lookup

This closes #2181.

`who --lookup` is failing with a runtime panic (double free).
Since `crate::dns-lookup` already includes a safe wrapper for `getaddrinfo`
I used this crate instead of further debugging the existing code in
utmpx::canon_host().

* It was neccessary to remove the version constraint for libc in uucore.
This commit is contained in:
Jan Scheer 2021-05-13 10:17:57 +02:00
parent a5f8ca60b5
commit a4fc2b5106
7 changed files with 79 additions and 65 deletions

29
Cargo.lock generated
View file

@ -576,6 +576,18 @@ dependencies = [
"generic-array",
]
[[package]]
name = "dns-lookup"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "093d88961fd18c4ecacb8c80cd0b356463ba941ba11e0e01f9cf5271380b79dc"
dependencies = [
"cfg-if 1.0.0",
"libc",
"socket2",
"winapi 0.3.9",
]
[[package]]
name = "dunce"
version = "1.0.1"
@ -1445,6 +1457,17 @@ dependencies = [
"maybe-uninit",
]
[[package]]
name = "socket2"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
dependencies = [
"cfg-if 1.0.0",
"libc",
"winapi 0.3.9",
]
[[package]]
name = "strsim"
version = "0.8.0"
@ -1820,11 +1843,9 @@ name = "uu_df"
version = "0.0.6"
dependencies = [
"clap",
"libc",
"number_prefix",
"uucore",
"uucore_procs",
"winapi 0.3.9",
]
[[package]]
@ -2407,8 +2428,6 @@ name = "uu_stat"
version = "0.0.6"
dependencies = [
"clap",
"libc",
"time",
"uucore",
"uucore_procs",
]
@ -2672,6 +2691,7 @@ name = "uucore"
version = "0.0.8"
dependencies = [
"data-encoding",
"dns-lookup",
"dunce",
"getopts",
"lazy_static",
@ -2682,6 +2702,7 @@ dependencies = [
"thiserror",
"time",
"wild",
"winapi 0.3.9",
]
[[package]]

View file

@ -286,17 +286,10 @@ impl Pinky {
print!(" {}", time_string(&ut));
if self.include_where && !ut.host().is_empty() {
let ut_host = ut.host();
let mut res = ut_host.splitn(2, ':');
let host = match res.next() {
Some(_) => ut.canon_host().unwrap_or_else(|_| ut_host.clone()),
None => ut_host.clone(),
};
match res.next() {
Some(d) => print!(" {}:{}", host, d),
None => print!(" {}", host),
}
let mut s = ut.host();
if self.include_where && !s.is_empty() {
s = safe_unwrap!(ut.canon_host());
print!(" {}", s);
}
println!();

View file

@ -548,20 +548,10 @@ impl Who {
" ?".into()
};
let mut buf = vec![];
let ut_host = ut.host();
let mut res = ut_host.splitn(2, ':');
if let Some(h) = res.next() {
if self.do_lookup {
buf.push(ut.canon_host().unwrap_or_else(|_| h.to_owned()));
} else {
buf.push(h.to_owned());
}
let mut s = ut.host();
if self.do_lookup {
s = safe_unwrap!(ut.canon_host());
}
if let Some(h) = res.next() {
buf.push(h.to_owned());
}
let s = buf.join(":");
let hoststr = if s.is_empty() { s } else { format!("({})", s) };
self.print_line(

View file

@ -16,6 +16,7 @@ edition = "2018"
path="src/lib/lib.rs"
[dependencies]
dns-lookup = "1.0.5"
dunce = "1.0.0"
getopts = "<= 0.2.21"
wild = "2.0.4"

View file

@ -188,47 +188,40 @@ impl Utmpx {
/// Canonicalize host name using DNS
pub fn canon_host(&self) -> IOResult<String> {
const AI_CANONNAME: libc::c_int = 0x2;
let host = self.host();
let host = host.split(':').next().unwrap();
let hints = libc::addrinfo {
ai_flags: AI_CANONNAME,
ai_family: 0,
ai_socktype: 0,
ai_protocol: 0,
ai_addrlen: 0,
ai_addr: ptr::null_mut(),
ai_canonname: ptr::null_mut(),
ai_next: ptr::null_mut(),
};
let c_host = CString::new(host).unwrap();
let mut res = ptr::null_mut();
let status = unsafe {
libc::getaddrinfo(
c_host.as_ptr(),
ptr::null(),
&hints as *const _,
&mut res as *mut _,
)
};
if status == 0 {
let info: libc::addrinfo = unsafe { ptr::read(res as *const _) };
// http://lists.gnu.org/archive/html/bug-coreutils/2006-09/msg00300.html
// says Darwin 7.9.0 getaddrinfo returns 0 but sets
// res->ai_canonname to NULL.
let ret = if info.ai_canonname.is_null() {
Ok(String::from(host))
} else {
Ok(unsafe { CString::from_raw(info.ai_canonname).into_string().unwrap() })
// TODO: change to use `split_once` when MSRV hits 1.52.0
// let (hostname, display) = host.split_once(':').unwrap_or((&host, ""));
let mut h = host.split(':');
let hostname = h.next().unwrap_or(&host);
let display = h.next().unwrap_or("");
if !hostname.is_empty() {
extern crate dns_lookup;
use dns_lookup::{getaddrinfo, AddrInfoHints};
const AI_CANONNAME: i32 = 0x2;
let hints = AddrInfoHints {
flags: AI_CANONNAME,
..AddrInfoHints::default()
};
unsafe {
libc::freeaddrinfo(res);
let sockets = getaddrinfo(Some(&hostname), None, Some(hints))
.unwrap()
.collect::<IOResult<Vec<_>>>()?;
for socket in sockets {
if let Some(ai_canonname) = socket.canonname {
return Ok(if display.is_empty() {
ai_canonname
} else {
format!("{}:{}", ai_canonname, display)
});
}
}
ret
} else {
Err(IOError::last_os_error())
}
Ok(host.to_string())
}
pub fn iter_all_records() -> UtmpxIter {
UtmpxIter
}

View file

@ -98,6 +98,23 @@ fn test_short_format_q() {
assert_eq!(v_actual, v_expect);
}
#[cfg(target_os = "linux")]
#[test]
fn test_no_flag() {
let scene = TestScenario::new(util_name!());
let actual = scene.ucmd().succeeds().stdout_move_str();
let expect = scene
.cmd_keepenv(util_name!())
.env("LANGUAGE", "C")
.succeeds()
.stdout_move_str();
let v_actual: Vec<&str> = actual.split_whitespace().collect();
let v_expect: Vec<&str> = expect.split_whitespace().collect();
assert_eq!(v_actual, v_expect);
}
#[cfg(target_os = "linux")]
fn expected_result(args: &[&str]) -> String {
TestScenario::new(util_name!())

View file

@ -162,7 +162,6 @@ fn test_users() {
#[cfg(target_os = "linux")]
#[test]
#[ignore]
fn test_lookup() {
for opt in vec!["--lookup"] {
new_ucmd!()