diff options
author | Geoffrey Thomas <geofft@ldpreload.com> | 2015-10-07 23:11:30 -0400 |
---|---|---|
committer | Carl Lerche <me@carllerche.com> | 2015-10-28 09:34:43 -0700 |
commit | ad87c3bdc99357f66af9260bce1eb6ae2cbfda98 (patch) | |
tree | 7cb4a8a57398ee4361261c711995a6484328848b /src | |
parent | 0cfa2a10a23712d44b4c745f4664fa3b46b9e943 (diff) | |
download | nix-ad87c3bdc99357f66af9260bce1eb6ae2cbfda98.zip |
Fix handling of sockaddr_un lengths
The returned length of AF_UNIX sockaddrs is significant, and generally
does not match the length of the entire structure. For filesystem
sockets, this is ignorable because the path is also NUL-terminated, but
for unbound sockets (e.g., a socketpair) or abstract-namespace
sockets (a Linux extension where the address is an arbitrary
bytestring), we need to keep track of the length.
Fixes #177. Also add a UnixAddr::new_abstract function and some better
handling of abstract-namespace socket addresses to fix #169.
Diffstat (limited to 'src')
-rw-r--r-- | src/sys/socket/addr.rs | 80 | ||||
-rw-r--r-- | src/sys/socket/mod.rs | 3 |
2 files changed, 63 insertions, 20 deletions
diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index 29c216eb..9a37176b 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -2,8 +2,8 @@ use {Result, Error, NixPath}; use super::{consts, sa_family_t}; use errno::Errno; use libc; -use std::{fmt, hash, mem, net}; -use std::ffi::{CStr, OsStr}; +use std::{fmt, hash, mem, net, ptr}; +use std::ffi::OsStr; use std::path::Path; use std::os::unix::ffi::OsStrExt; @@ -329,13 +329,16 @@ impl fmt::Display for Ipv6Addr { * */ +/// A wrapper around sockaddr_un. We track the length of sun_path, +/// because it may not be null-terminated (unconnected and abstract +/// sockets). Note that the actual sockaddr length is greater by +/// size_of::<sa_family_t>(). #[derive(Copy)] -pub struct UnixAddr(pub libc::sockaddr_un); +pub struct UnixAddr(pub libc::sockaddr_un, pub usize); impl UnixAddr { + /// Create a new sockaddr_un representing a filesystem path. pub fn new<P: ?Sized + NixPath>(path: &P) -> Result<UnixAddr> { - use libc::strcpy; - try!(path.with_nix_path(|cstr| { unsafe { let mut ret = libc::sockaddr_un { @@ -343,31 +346,65 @@ impl UnixAddr { .. mem::zeroed() }; - // Must be smaller to account for the null byte - if path.len() >= ret.sun_path.len() { + let bytes = cstr.to_bytes_with_nul(); + + if bytes.len() > ret.sun_path.len() { return Err(Error::Sys(Errno::ENAMETOOLONG)); } - strcpy(ret.sun_path.as_mut_ptr(), cstr.as_ptr()); + ptr::copy_nonoverlapping(bytes.as_ptr(), + ret.sun_path.as_mut_ptr() as *mut u8, + bytes.len()); - Ok(UnixAddr(ret)) + Ok(UnixAddr(ret, bytes.len())) } })) } - pub fn path(&self) -> &Path { + /// Create a new sockaddr_un representing an address in the + /// "abstract namespace". This is a Linux-specific extension, + /// primarily used to allow chrooted processes to communicate with + /// specific daemons. + pub fn new_abstract(path: &[u8]) -> Result<UnixAddr> { unsafe { - let bytes = CStr::from_ptr(self.0.sun_path.as_ptr()).to_bytes(); - Path::new(<OsStr as OsStrExt>::from_bytes(bytes)) + let mut ret = libc::sockaddr_un { + sun_family: AddressFamily::Unix as sa_family_t, + .. mem::zeroed() + }; + + if path.len() > ret.sun_path.len() { + return Err(Error::Sys(Errno::ENAMETOOLONG)); + } + + // Abstract addresses are represented by sun_path[0] == + // b'\0', so copy starting one byte in. + ptr::copy_nonoverlapping(path.as_ptr(), + ret.sun_path.as_mut_ptr().offset(1) as *mut u8, + path.len()); + + Ok(UnixAddr(ret, path.len())) + } + } + + fn sun_path(&self) -> &[u8] { + unsafe { mem::transmute(&self.0.sun_path[..self.1]) } + } + + /// If this address represents a filesystem path, return that path. + pub fn path(&self) -> Option<&Path> { + if self.1 == 0 || self.0.sun_path[0] == 0 { + // unbound or abstract + None + } else { + let p = self.sun_path(); + Some(Path::new(<OsStr as OsStrExt>::from_bytes(&p[..p.len()-1]))) } } } impl PartialEq for UnixAddr { fn eq(&self, other: &UnixAddr) -> bool { - unsafe { - 0 == libc::strcmp(self.0.sun_path.as_ptr(), other.0.sun_path.as_ptr()) - } + self.sun_path() == other.sun_path() } } @@ -376,7 +413,7 @@ impl Eq for UnixAddr { impl hash::Hash for UnixAddr { fn hash<H: hash::Hasher>(&self, s: &mut H) { - ( self.0.sun_family, self.path() ).hash(s) + ( self.0.sun_family, self.sun_path() ).hash(s) } } @@ -388,7 +425,14 @@ impl Clone for UnixAddr { impl fmt::Display for UnixAddr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.path().display().fmt(f) + if self.1 == 0 { + f.write_str("<unbound UNIX socket>") + } else if let Some(path) = self.path() { + path.display().fmt(f) + } else { + let display = String::from_utf8_lossy(&self.sun_path()[1..]); + write!(f, "@{}", display) + } } } @@ -430,7 +474,7 @@ impl SockAddr { match *self { SockAddr::Inet(InetAddr::V4(ref addr)) => (mem::transmute(addr), mem::size_of::<libc::sockaddr_in>() as libc::socklen_t), SockAddr::Inet(InetAddr::V6(ref addr)) => (mem::transmute(addr), mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t), - SockAddr::Unix(UnixAddr(ref addr)) => (mem::transmute(addr), mem::size_of::<libc::sockaddr_un>() as libc::socklen_t), + SockAddr::Unix(UnixAddr(ref addr, len)) => (mem::transmute(addr), (len + mem::size_of::<libc::sa_family_t>()) as libc::socklen_t), } } } diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 39c38202..8a6924d2 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -687,8 +687,7 @@ pub unsafe fn sockaddr_storage_to_addr( Ok(SockAddr::Inet(InetAddr::V6((*(addr as *const _ as *const sockaddr_in6))))) } consts::AF_UNIX => { - assert!(len as usize == mem::size_of::<sockaddr_un>()); - Ok(SockAddr::Unix(UnixAddr(*(addr as *const _ as *const sockaddr_un)))) + Ok(SockAddr::Unix(UnixAddr(*(addr as *const _ as *const sockaddr_un), len))) } af => panic!("unexpected address family {}", af), } |