diff options
Diffstat (limited to 'third_party/rust/mio-uds/src/socket.rs')
-rw-r--r-- | third_party/rust/mio-uds/src/socket.rs | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/third_party/rust/mio-uds/src/socket.rs b/third_party/rust/mio-uds/src/socket.rs new file mode 100644 index 0000000000..820f74dde7 --- /dev/null +++ b/third_party/rust/mio-uds/src/socket.rs @@ -0,0 +1,166 @@ +use std::cmp::Ordering; +use std::io; +use std::mem; +use std::os::unix::prelude::*; +use std::path::Path; + +use libc::{self, c_int, c_ulong}; + +use cvt; + +// See below for the usage of SOCK_CLOEXEC, but this constant is only defined on +// Linux currently (e.g. support doesn't exist on other platforms). In order to +// get name resolution to work and things to compile we just define a dummy +// SOCK_CLOEXEC here for other platforms. Note that the dummy constant isn't +// actually ever used (the blocks below are wrapped in `if cfg!` as well. +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "illumos", + target_os = "solaris" +))] +use libc::{SOCK_CLOEXEC, SOCK_NONBLOCK}; +#[cfg(not(any( + target_os = "linux", + target_os = "android", + target_os = "illumos", + target_os = "solaris" +)))] +const SOCK_CLOEXEC: c_int = 0; +#[cfg(not(any( + target_os = "linux", + target_os = "android", + target_os = "illumos", + target_os = "solaris" +)))] +const SOCK_NONBLOCK: c_int = 0; + +pub struct Socket { + fd: c_int, +} + +impl Socket { + pub fn new(ty: c_int) -> io::Result<Socket> { + unsafe { + // On linux we first attempt to pass the SOCK_CLOEXEC flag to + // atomically create the socket and set it as CLOEXEC. Support for + // this option, however, was added in 2.6.27, and we still support + // 2.6.18 as a kernel, so if the returned error is EINVAL we + // fallthrough to the fallback. + if cfg!(any( + target_os = "linux", + target_os = "android", + target_os = "illumos", + target_os = "solaris" + )) { + let flags = ty | SOCK_CLOEXEC | SOCK_NONBLOCK; + match cvt(libc::socket(libc::AF_UNIX, flags, 0)) { + Ok(fd) => return Ok(Socket { fd: fd }), + Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {} + Err(e) => return Err(e), + } + } + + let fd = Socket { fd: try!(cvt(libc::socket(libc::AF_UNIX, ty, 0))) }; + try!(cvt(libc::ioctl(fd.fd, libc::FIOCLEX))); + let mut nonblocking = 1 as c_ulong; + try!(cvt(libc::ioctl(fd.fd, libc::FIONBIO, &mut nonblocking))); + Ok(fd) + } + } + + pub fn pair(ty: c_int) -> io::Result<(Socket, Socket)> { + unsafe { + let mut fds = [0, 0]; + + // Like above, see if we can set cloexec atomically + if cfg!(any( + target_os = "linux", + target_os = "android", + target_os = "illumos", + target_os = "solaris" + )) { + let flags = ty | SOCK_CLOEXEC | SOCK_NONBLOCK; + match cvt(libc::socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr())) { + Ok(_) => { + return Ok((Socket { fd: fds[0] }, Socket { fd: fds[1] })) + } + Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}, + Err(e) => return Err(e), + } + } + + try!(cvt(libc::socketpair(libc::AF_UNIX, ty, 0, fds.as_mut_ptr()))); + let a = Socket { fd: fds[0] }; + let b = Socket { fd: fds[1] }; + try!(cvt(libc::ioctl(a.fd, libc::FIOCLEX))); + try!(cvt(libc::ioctl(b.fd, libc::FIOCLEX))); + let mut nonblocking = 1 as c_ulong; + try!(cvt(libc::ioctl(a.fd, libc::FIONBIO, &mut nonblocking))); + try!(cvt(libc::ioctl(b.fd, libc::FIONBIO, &mut nonblocking))); + Ok((a, b)) + } + } + + pub fn fd(&self) -> c_int { + self.fd + } + + pub fn into_fd(self) -> c_int { + let ret = self.fd; + mem::forget(self); + ret + } +} + +impl Drop for Socket { + fn drop(&mut self) { + unsafe { + let _ = libc::close(self.fd); + } + } +} + +pub unsafe fn sockaddr_un(path: &Path) + -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { + let mut addr: libc::sockaddr_un = mem::zeroed(); + addr.sun_family = libc::AF_UNIX as libc::sa_family_t; + + let bytes = path.as_os_str().as_bytes(); + + match (bytes.get(0), bytes.len().cmp(&addr.sun_path.len())) { + // Abstract paths don't need a null terminator + (Some(&0), Ordering::Greater) => { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "path must be no longer than SUN_LEN")); + } + (_, Ordering::Greater) | (_, Ordering::Equal) => { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "path must be shorter than SUN_LEN")); + } + _ => {} + } + for (dst, src) in addr.sun_path.iter_mut().zip(bytes.iter()) { + *dst = *src as libc::c_char; + } + // null byte for pathname addresses is already there because we zeroed the + // struct + + let mut len = sun_path_offset() + bytes.len(); + match bytes.get(0) { + Some(&0) | None => {} + Some(_) => len += 1, + } + Ok((addr, len as libc::socklen_t)) +} + +fn sun_path_offset() -> usize { + unsafe { + // Work with an actual instance of the type since using a null pointer is UB + let addr: libc::sockaddr_un = mem::uninitialized(); + let base = &addr as *const _ as usize; + let path = &addr.sun_path as *const _ as usize; + path - base + } +} + |