diff options
Diffstat (limited to 'third_party/rust/nix/src/sys/socket')
-rw-r--r-- | third_party/rust/nix/src/sys/socket/addr.rs | 1408 | ||||
-rw-r--r-- | third_party/rust/nix/src/sys/socket/mod.rs | 1204 | ||||
-rw-r--r-- | third_party/rust/nix/src/sys/socket/sockopt.rs | 631 |
3 files changed, 3243 insertions, 0 deletions
diff --git a/third_party/rust/nix/src/sys/socket/addr.rs b/third_party/rust/nix/src/sys/socket/addr.rs new file mode 100644 index 0000000000..9ec668985b --- /dev/null +++ b/third_party/rust/nix/src/sys/socket/addr.rs @@ -0,0 +1,1408 @@ +use super::sa_family_t; +use {Error, Result, NixPath}; +use errno::Errno; +use libc; +use std::{fmt, hash, mem, net, ptr, slice}; +use std::ffi::OsStr; +use std::path::Path; +use std::os::unix::ffi::OsStrExt; +#[cfg(any(target_os = "android", target_os = "linux"))] +use ::sys::socket::addr::netlink::NetlinkAddr; +#[cfg(any(target_os = "ios", target_os = "macos"))] +use std::os::unix::io::RawFd; +#[cfg(any(target_os = "ios", target_os = "macos"))] +use ::sys::socket::addr::sys_control::SysControlAddr; +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +pub use self::datalink::LinkAddr; + +/// These constants specify the protocol family to be used +/// in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html) +#[repr(i32)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +pub enum AddressFamily { + /// Local communication (see [`unix(7)`](http://man7.org/linux/man-pages/man7/unix.7.html)) + Unix = libc::AF_UNIX, + /// IPv4 Internet protocols (see [`ip(7)`](http://man7.org/linux/man-pages/man7/ip.7.html)) + Inet = libc::AF_INET, + /// IPv6 Internet protocols (see [`ipv6(7)`](http://man7.org/linux/man-pages/man7/ipv6.7.html)) + Inet6 = libc::AF_INET6, + /// Kernel user interface device (see [`netlink(7)`](http://man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + Netlink = libc::AF_NETLINK, + /// Low level packet interface (see [`packet(7)`](http://man7.org/linux/man-pages/man7/packet.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + Packet = libc::AF_PACKET, + /// KEXT Controls and Notifications + #[cfg(any(target_os = "ios", target_os = "macos"))] + System = libc::AF_SYSTEM, + /// Amateur radio AX.25 protocol + #[cfg(any(target_os = "android", target_os = "linux"))] + Ax25 = libc::AF_AX25, + /// IPX - Novell protocols + Ipx = libc::AF_IPX, + /// AppleTalk + AppleTalk = libc::AF_APPLETALK, + #[cfg(any(target_os = "android", target_os = "linux"))] + NetRom = libc::AF_NETROM, + #[cfg(any(target_os = "android", target_os = "linux"))] + Bridge = libc::AF_BRIDGE, + /// Access to raw ATM PVCs + #[cfg(any(target_os = "android", target_os = "linux"))] + AtmPvc = libc::AF_ATMPVC, + /// ITU-T X.25 / ISO-8208 protocol (see [`x25(7)`](http://man7.org/linux/man-pages/man7/x25.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + X25 = libc::AF_X25, + #[cfg(any(target_os = "android", target_os = "linux"))] + Rose = libc::AF_ROSE, + Decnet = libc::AF_DECnet, + #[cfg(any(target_os = "android", target_os = "linux"))] + NetBeui = libc::AF_NETBEUI, + #[cfg(any(target_os = "android", target_os = "linux"))] + Security = libc::AF_SECURITY, + #[cfg(any(target_os = "android", target_os = "linux"))] + Key = libc::AF_KEY, + #[cfg(any(target_os = "android", target_os = "linux"))] + Ash = libc::AF_ASH, + #[cfg(any(target_os = "android", target_os = "linux"))] + Econet = libc::AF_ECONET, + #[cfg(any(target_os = "android", target_os = "linux"))] + AtmSvc = libc::AF_ATMSVC, + #[cfg(any(target_os = "android", target_os = "linux"))] + Rds = libc::AF_RDS, + Sna = libc::AF_SNA, + #[cfg(any(target_os = "android", target_os = "linux"))] + Irda = libc::AF_IRDA, + #[cfg(any(target_os = "android", target_os = "linux"))] + Pppox = libc::AF_PPPOX, + #[cfg(any(target_os = "android", target_os = "linux"))] + Wanpipe = libc::AF_WANPIPE, + #[cfg(any(target_os = "android", target_os = "linux"))] + Llc = libc::AF_LLC, + #[cfg(target_os = "linux")] + Ib = libc::AF_IB, + #[cfg(target_os = "linux")] + Mpls = libc::AF_MPLS, + #[cfg(any(target_os = "android", target_os = "linux"))] + Can = libc::AF_CAN, + #[cfg(any(target_os = "android", target_os = "linux"))] + Tipc = libc::AF_TIPC, + #[cfg(not(any(target_os = "ios", target_os = "macos")))] + Bluetooth = libc::AF_BLUETOOTH, + #[cfg(any(target_os = "android", target_os = "linux"))] + Iucv = libc::AF_IUCV, + #[cfg(any(target_os = "android", target_os = "linux"))] + RxRpc = libc::AF_RXRPC, + Isdn = libc::AF_ISDN, + #[cfg(any(target_os = "android", target_os = "linux"))] + Phonet = libc::AF_PHONET, + #[cfg(any(target_os = "android", target_os = "linux"))] + Ieee802154 = libc::AF_IEEE802154, + #[cfg(any(target_os = "android", target_os = "linux"))] + Caif = libc::AF_CAIF, + /// Interface to kernel crypto API + #[cfg(any(target_os = "android", target_os = "linux"))] + Alg = libc::AF_ALG, + #[cfg(target_os = "linux")] + Nfc = libc::AF_NFC, + #[cfg(target_os = "linux")] + Vsock = libc::AF_VSOCK, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + ImpLink = libc::AF_IMPLINK, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Pup = libc::AF_PUP, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Chaos = libc::AF_CHAOS, + #[cfg(any(target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Ns = libc::AF_NS, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Iso = libc::AF_ISO, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Datakit = libc::AF_DATAKIT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Ccitt = libc::AF_CCITT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Dli = libc::AF_DLI, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Lat = libc::AF_LAT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Hylink = libc::AF_HYLINK, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Link = libc::AF_LINK, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Coip = libc::AF_COIP, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Cnt = libc::AF_CNT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Natm = libc::AF_NATM, + /// Unspecified address family, (see [`getaddrinfo(3)`](http://man7.org/linux/man-pages/man3/getaddrinfo.3.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + Unspec = libc::AF_UNSPEC, +} + +impl AddressFamily { + /// Create a new `AddressFamily` from an integer value retrieved from `libc`, usually from + /// the `sa_family` field of a `sockaddr`. + /// + /// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink, Link/Packet + /// and System. Returns None for unsupported or unknown address families. + pub fn from_i32(family: i32) -> Option<AddressFamily> { + match family { + libc::AF_UNIX => Some(AddressFamily::Unix), + libc::AF_INET => Some(AddressFamily::Inet), + libc::AF_INET6 => Some(AddressFamily::Inet6), + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_NETLINK => Some(AddressFamily::Netlink), + #[cfg(any(target_os = "macos", target_os = "macos"))] + libc::AF_SYSTEM => Some(AddressFamily::System), + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_PACKET => Some(AddressFamily::Packet), + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + libc::AF_LINK => Some(AddressFamily::Link), + _ => None + } + } +} + +#[derive(Copy)] +pub enum InetAddr { + V4(libc::sockaddr_in), + V6(libc::sockaddr_in6), +} + +impl InetAddr { + pub fn from_std(std: &net::SocketAddr) -> InetAddr { + match *std { + net::SocketAddr::V4(ref addr) => { + InetAddr::V4(libc::sockaddr_in { + sin_family: AddressFamily::Inet as sa_family_t, + sin_port: addr.port().to_be(), // network byte order + sin_addr: Ipv4Addr::from_std(addr.ip()).0, + .. unsafe { mem::zeroed() } + }) + } + net::SocketAddr::V6(ref addr) => { + InetAddr::V6(libc::sockaddr_in6 { + sin6_family: AddressFamily::Inet6 as sa_family_t, + sin6_port: addr.port().to_be(), // network byte order + sin6_addr: Ipv6Addr::from_std(addr.ip()).0, + sin6_flowinfo: addr.flowinfo(), // host byte order + sin6_scope_id: addr.scope_id(), // host byte order + .. unsafe { mem::zeroed() } + }) + } + } + } + + pub fn new(ip: IpAddr, port: u16) -> InetAddr { + match ip { + IpAddr::V4(ref ip) => { + InetAddr::V4(libc::sockaddr_in { + sin_family: AddressFamily::Inet as sa_family_t, + sin_port: port.to_be(), + sin_addr: ip.0, + .. unsafe { mem::zeroed() } + }) + } + IpAddr::V6(ref ip) => { + InetAddr::V6(libc::sockaddr_in6 { + sin6_family: AddressFamily::Inet6 as sa_family_t, + sin6_port: port.to_be(), + sin6_addr: ip.0, + .. unsafe { mem::zeroed() } + }) + } + } + } + /// Gets the IP address associated with this socket address. + pub fn ip(&self) -> IpAddr { + match *self { + InetAddr::V4(ref sa) => IpAddr::V4(Ipv4Addr(sa.sin_addr)), + InetAddr::V6(ref sa) => IpAddr::V6(Ipv6Addr(sa.sin6_addr)), + } + } + + /// Gets the port number associated with this socket address + pub fn port(&self) -> u16 { + match *self { + InetAddr::V6(ref sa) => u16::from_be(sa.sin6_port), + InetAddr::V4(ref sa) => u16::from_be(sa.sin_port), + } + } + + pub fn to_std(&self) -> net::SocketAddr { + match *self { + InetAddr::V4(ref sa) => net::SocketAddr::V4( + net::SocketAddrV4::new( + Ipv4Addr(sa.sin_addr).to_std(), + self.port())), + InetAddr::V6(ref sa) => net::SocketAddr::V6( + net::SocketAddrV6::new( + Ipv6Addr(sa.sin6_addr).to_std(), + self.port(), + sa.sin6_flowinfo, + sa.sin6_scope_id)), + } + } + + pub fn to_str(&self) -> String { + format!("{}", self) + } +} + +impl PartialEq for InetAddr { + fn eq(&self, other: &InetAddr) -> bool { + match (*self, *other) { + (InetAddr::V4(ref a), InetAddr::V4(ref b)) => { + a.sin_port == b.sin_port && + a.sin_addr.s_addr == b.sin_addr.s_addr + } + (InetAddr::V6(ref a), InetAddr::V6(ref b)) => { + a.sin6_port == b.sin6_port && + a.sin6_addr.s6_addr == b.sin6_addr.s6_addr && + a.sin6_flowinfo == b.sin6_flowinfo && + a.sin6_scope_id == b.sin6_scope_id + } + _ => false, + } + } +} + +impl Eq for InetAddr { +} + +impl hash::Hash for InetAddr { + fn hash<H: hash::Hasher>(&self, s: &mut H) { + match *self { + InetAddr::V4(ref a) => { + ( a.sin_family, + a.sin_port, + a.sin_addr.s_addr ).hash(s) + } + InetAddr::V6(ref a) => { + ( a.sin6_family, + a.sin6_port, + &a.sin6_addr.s6_addr, + a.sin6_flowinfo, + a.sin6_scope_id ).hash(s) + } + } + } +} + +impl Clone for InetAddr { + fn clone(&self) -> InetAddr { + *self + } +} + +impl fmt::Display for InetAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + InetAddr::V4(_) => write!(f, "{}:{}", self.ip(), self.port()), + InetAddr::V6(_) => write!(f, "[{}]:{}", self.ip(), self.port()), + } + } +} + +impl fmt::Debug for InetAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/* + * + * ===== IpAddr ===== + * + */ +#[derive(Clone, Copy)] +pub enum IpAddr { + V4(Ipv4Addr), + V6(Ipv6Addr), +} + +impl IpAddr { + /// Create a new IpAddr that contains an IPv4 address. + /// + /// The result will represent the IP address a.b.c.d + pub fn new_v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr { + IpAddr::V4(Ipv4Addr::new(a, b, c, d)) + } + + /// Create a new IpAddr that contains an IPv6 address. + /// + /// The result will represent the IP address a:b:c:d:e:f + pub fn new_v6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> IpAddr { + IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h)) + } + + /* + pub fn from_std(std: &net::IpAddr) -> IpAddr { + match *std { + net::IpAddr::V4(ref std) => IpAddr::V4(Ipv4Addr::from_std(std)), + net::IpAddr::V6(ref std) => IpAddr::V6(Ipv6Addr::from_std(std)), + } + } + pub fn to_std(&self) -> net::IpAddr { + match *self { + IpAddr::V4(ref ip) => net::IpAddr::V4(ip.to_std()), + IpAddr::V6(ref ip) => net::IpAddr::V6(ip.to_std()), + } + } + */ +} + +impl fmt::Display for IpAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + IpAddr::V4(ref v4) => v4.fmt(f), + IpAddr::V6(ref v6) => v6.fmt(f) + } + } +} + +impl fmt::Debug for IpAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/* + * + * ===== Ipv4Addr ===== + * + */ + +#[derive(Copy)] +pub struct Ipv4Addr(pub libc::in_addr); + +impl Ipv4Addr { + pub fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { + let ip = (((a as u32) << 24) | + ((b as u32) << 16) | + ((c as u32) << 8) | + ((d as u32) << 0)).to_be(); + + Ipv4Addr(libc::in_addr { s_addr: ip }) + } + + pub fn from_std(std: &net::Ipv4Addr) -> Ipv4Addr { + let bits = std.octets(); + Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3]) + } + + pub fn any() -> Ipv4Addr { + Ipv4Addr(libc::in_addr { s_addr: libc::INADDR_ANY }) + } + + pub fn octets(&self) -> [u8; 4] { + let bits = u32::from_be(self.0.s_addr); + [(bits >> 24) as u8, (bits >> 16) as u8, (bits >> 8) as u8, bits as u8] + } + + pub fn to_std(&self) -> net::Ipv4Addr { + let bits = self.octets(); + net::Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3]) + } +} + +impl PartialEq for Ipv4Addr { + fn eq(&self, other: &Ipv4Addr) -> bool { + self.0.s_addr == other.0.s_addr + } +} + +impl Eq for Ipv4Addr { +} + +impl hash::Hash for Ipv4Addr { + fn hash<H: hash::Hasher>(&self, s: &mut H) { + let saddr = self.0.s_addr; + saddr.hash(s) + } +} + +impl Clone for Ipv4Addr { + fn clone(&self) -> Ipv4Addr { + *self + } +} + +impl fmt::Display for Ipv4Addr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let octets = self.octets(); + write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) + } +} + +impl fmt::Debug for Ipv4Addr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/* + * + * ===== Ipv6Addr ===== + * + */ + +#[derive(Clone, Copy)] +pub struct Ipv6Addr(pub libc::in6_addr); + +// Note that IPv6 addresses are stored in big endian order on all architectures. +// See https://tools.ietf.org/html/rfc1700 or consult your favorite search +// engine. + +macro_rules! to_u8_array { + ($($num:ident),*) => { + [ $(($num>>8) as u8, ($num&0xff) as u8,)* ] + } +} + +macro_rules! to_u16_array { + ($slf:ident, $($first:expr, $second:expr),*) => { + [$( (($slf.0.s6_addr[$first] as u16) << 8) + $slf.0.s6_addr[$second] as u16,)*] + } +} + +impl Ipv6Addr { + pub fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { + let mut in6_addr_var: libc::in6_addr = unsafe{mem::uninitialized()}; + in6_addr_var.s6_addr = to_u8_array!(a,b,c,d,e,f,g,h); + Ipv6Addr(in6_addr_var) + } + + pub fn from_std(std: &net::Ipv6Addr) -> Ipv6Addr { + let s = std.segments(); + Ipv6Addr::new(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]) + } + + /// Return the eight 16-bit segments that make up this address + pub fn segments(&self) -> [u16; 8] { + to_u16_array!(self, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15) + } + + pub fn to_std(&self) -> net::Ipv6Addr { + let s = self.segments(); + net::Ipv6Addr::new(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]) + } +} + +impl fmt::Display for Ipv6Addr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.to_std().fmt(fmt) + } +} + +impl fmt::Debug for Ipv6Addr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/* + * + * ===== UnixAddr ===== + * + */ + +/// A wrapper around `sockaddr_un`. +/// +/// This also tracks the length of `sun_path` address (excluding +/// a terminating null), because it may not be null-terminated. For example, +/// unconnected and Linux abstract sockets are never null-terminated, and POSIX +/// does not require that `sun_len` include the terminating null even for normal +/// sockets. Note that the actual sockaddr length is greater by +/// `offset_of!(libc::sockaddr_un, sun_path)` +#[derive(Copy)] +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> { + path.with_nix_path(|cstr| { + unsafe { + let mut ret = libc::sockaddr_un { + sun_family: AddressFamily::Unix as sa_family_t, + .. mem::zeroed() + }; + + let bytes = cstr.to_bytes(); + + if bytes.len() > ret.sun_path.len() { + return Err(Error::Sys(Errno::ENAMETOOLONG)); + } + + ptr::copy_nonoverlapping(bytes.as_ptr(), + ret.sun_path.as_mut_ptr() as *mut u8, + bytes.len()); + + Ok(UnixAddr(ret, bytes.len())) + } + })? + } + + /// Create a new `sockaddr_un` representing an address in the "abstract namespace". + /// + /// The leading null byte for the abstract namespace is automatically added; + /// thus the input `path` is expected to be the bare name, not null-prefixed. + /// This is a Linux-specific extension, primarily used to allow chrooted + /// processes to communicate with processes having a different filesystem view. + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn new_abstract(path: &[u8]) -> Result<UnixAddr> { + unsafe { + let mut ret = libc::sockaddr_un { + sun_family: AddressFamily::Unix as sa_family_t, + .. mem::zeroed() + }; + + if path.len() + 1 > 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() + 1)) + } + } + + fn sun_path(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.0.sun_path.as_ptr() as *const u8, 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 { + // unnamed or abstract + None + } else { + let p = self.sun_path(); + // POSIX only requires that `sun_len` be at least long enough to + // contain the pathname, and it need not be null-terminated. So we + // need to create a string that is the shorter of the + // null-terminated length or the full length. + let ptr = &self.0.sun_path as *const libc::c_char; + let reallen = unsafe { libc::strnlen(ptr, p.len()) }; + Some(Path::new(<OsStr as OsStrExt>::from_bytes(&p[..reallen]))) + } + } + + /// If this address represents an abstract socket, return its name. + /// + /// For abstract sockets only the bare name is returned, without the + /// leading null byte. `None` is returned for unnamed or path-backed sockets. + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn as_abstract(&self) -> Option<&[u8]> { + if self.1 >= 1 && self.0.sun_path[0] == 0 { + Some(&self.sun_path()[1..]) + } else { + // unnamed or filesystem path + None + } + } +} + +impl PartialEq for UnixAddr { + fn eq(&self, other: &UnixAddr) -> bool { + self.sun_path() == other.sun_path() + } +} + +impl Eq for UnixAddr { +} + +impl hash::Hash for UnixAddr { + fn hash<H: hash::Hasher>(&self, s: &mut H) { + ( self.0.sun_family, self.sun_path() ).hash(s) + } +} + +impl Clone for UnixAddr { + fn clone(&self) -> UnixAddr { + *self + } +} + +impl fmt::Display for UnixAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + 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) + } + } +} + +impl fmt::Debug for UnixAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/* + * + * ===== Sock addr ===== + * + */ + +/// Represents a socket address +#[derive(Copy, Debug)] +pub enum SockAddr { + Inet(InetAddr), + Unix(UnixAddr), + #[cfg(any(target_os = "android", target_os = "linux"))] + Netlink(NetlinkAddr), + #[cfg(any(target_os = "ios", target_os = "macos"))] + SysControl(SysControlAddr), + /// Datalink address (MAC) + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Link(LinkAddr) +} + +impl SockAddr { + pub fn new_inet(addr: InetAddr) -> SockAddr { + SockAddr::Inet(addr) + } + + pub fn new_unix<P: ?Sized + NixPath>(path: &P) -> Result<SockAddr> { + Ok(SockAddr::Unix(UnixAddr::new(path)?)) + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn new_netlink(pid: u32, groups: u32) -> SockAddr { + SockAddr::Netlink(NetlinkAddr::new(pid, groups)) + } + + #[cfg(any(target_os = "ios", target_os = "macos"))] + pub fn new_sys_control(sockfd: RawFd, name: &str, unit: u32) -> Result<SockAddr> { + SysControlAddr::from_name(sockfd, name, unit).map(|a| SockAddr::SysControl(a)) + } + + pub fn family(&self) -> AddressFamily { + match *self { + SockAddr::Inet(InetAddr::V4(..)) => AddressFamily::Inet, + SockAddr::Inet(InetAddr::V6(..)) => AddressFamily::Inet6, + SockAddr::Unix(..) => AddressFamily::Unix, + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Netlink(..) => AddressFamily::Netlink, + #[cfg(any(target_os = "ios", target_os = "macos"))] + SockAddr::SysControl(..) => AddressFamily::System, + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Link(..) => AddressFamily::Packet, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + SockAddr::Link(..) => AddressFamily::Link + } + } + + pub fn to_str(&self) -> String { + format!("{}", self) + } + + /// Creates a `SockAddr` struct from libc's sockaddr. + /// + /// Supports only the following address families: Unix, Inet (v4 & v6), Netlink and System. + /// Returns None for unsupported families. + pub unsafe fn from_libc_sockaddr(addr: *const libc::sockaddr) -> Option<SockAddr> { + if addr.is_null() { + None + } else { + match AddressFamily::from_i32((*addr).sa_family as i32) { + Some(AddressFamily::Unix) => None, + Some(AddressFamily::Inet) => Some(SockAddr::Inet( + InetAddr::V4(*(addr as *const libc::sockaddr_in)))), + Some(AddressFamily::Inet6) => Some(SockAddr::Inet( + InetAddr::V6(*(addr as *const libc::sockaddr_in6)))), + #[cfg(any(target_os = "android", target_os = "linux"))] + Some(AddressFamily::Netlink) => Some(SockAddr::Netlink( + NetlinkAddr(*(addr as *const libc::sockaddr_nl)))), + #[cfg(any(target_os = "ios", target_os = "macos"))] + Some(AddressFamily::System) => Some(SockAddr::SysControl( + SysControlAddr(*(addr as *const libc::sockaddr_ctl)))), + #[cfg(any(target_os = "android", target_os = "linux"))] + Some(AddressFamily::Packet) => Some(SockAddr::Link( + LinkAddr(*(addr as *const libc::sockaddr_ll)))), + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Some(AddressFamily::Link) => { + let ether_addr = LinkAddr(*(addr as *const libc::sockaddr_dl)); + if ether_addr.is_empty() { + None + } else { + Some(SockAddr::Link(ether_addr)) + } + }, + // Other address families are currently not supported and simply yield a None + // entry instead of a proper conversion to a `SockAddr`. + Some(_) | None => None, + } + } + } + + /// Conversion from nix's SockAddr type to the underlying libc sockaddr type. + /// + /// This is useful for interfacing with other libc functions that don't yet have nix wrappers. + /// Returns a reference to the underlying data type (as a sockaddr reference) along + /// with the size of the actual data type. sockaddr is commonly used as a proxy for + /// a superclass as C doesn't support inheritance, so many functions that take + /// a sockaddr * need to take the size of the underlying type as well and then internally cast it back. + pub unsafe fn as_ffi_pair(&self) -> (&libc::sockaddr, libc::socklen_t) { + 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, len)) => (mem::transmute(addr), (len + offset_of!(libc::sockaddr_un, sun_path)) as libc::socklen_t), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Netlink(NetlinkAddr(ref sa)) => (mem::transmute(sa), mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t), + #[cfg(any(target_os = "ios", target_os = "macos"))] + SockAddr::SysControl(SysControlAddr(ref sa)) => (mem::transmute(sa), mem::size_of::<libc::sockaddr_ctl>() as libc::socklen_t), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Link(LinkAddr(ref ether_addr)) => (mem::transmute(ether_addr), mem::size_of::<libc::sockaddr_ll>() as libc::socklen_t), + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + SockAddr::Link(LinkAddr(ref ether_addr)) => (mem::transmute(ether_addr), mem::size_of::<libc::sockaddr_dl>() as libc::socklen_t), + } + } +} + +impl PartialEq for SockAddr { + fn eq(&self, other: &SockAddr) -> bool { + match (*self, *other) { + (SockAddr::Inet(ref a), SockAddr::Inet(ref b)) => { + a == b + } + (SockAddr::Unix(ref a), SockAddr::Unix(ref b)) => { + a == b + } + #[cfg(any(target_os = "android", target_os = "linux"))] + (SockAddr::Netlink(ref a), SockAddr::Netlink(ref b)) => { + a == b + } + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + (SockAddr::Link(ref a), SockAddr::Link(ref b)) => { + a == b + } + _ => false, + } + } +} + +impl Eq for SockAddr { +} + +impl hash::Hash for SockAddr { + fn hash<H: hash::Hasher>(&self, s: &mut H) { + match *self { + SockAddr::Inet(ref a) => a.hash(s), + SockAddr::Unix(ref a) => a.hash(s), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Netlink(ref a) => a.hash(s), + #[cfg(any(target_os = "ios", target_os = "macos"))] + SockAddr::SysControl(ref a) => a.hash(s), + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + SockAddr::Link(ref ether_addr) => ether_addr.hash(s) + } + } +} + +impl Clone for SockAddr { + fn clone(&self) -> SockAddr { + *self + } +} + +impl fmt::Display for SockAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + SockAddr::Inet(ref inet) => inet.fmt(f), + SockAddr::Unix(ref unix) => unix.fmt(f), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Netlink(ref nl) => nl.fmt(f), + #[cfg(any(target_os = "ios", target_os = "macos"))] + SockAddr::SysControl(ref sc) => sc.fmt(f), + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + SockAddr::Link(ref ether_addr) => ether_addr.fmt(f) + } + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub mod netlink { + use ::sys::socket::addr::AddressFamily; + use libc::{sa_family_t, sockaddr_nl}; + use std::{fmt, mem}; + use std::hash::{Hash, Hasher}; + + #[derive(Copy, Clone)] + pub struct NetlinkAddr(pub sockaddr_nl); + + // , PartialEq, Eq, Debug, Hash + impl PartialEq for NetlinkAddr { + fn eq(&self, other: &Self) -> bool { + let (inner, other) = (self.0, other.0); + (inner.nl_family, inner.nl_pid, inner.nl_groups) == + (other.nl_family, other.nl_pid, other.nl_groups) + } + } + + impl Eq for NetlinkAddr {} + + impl Hash for NetlinkAddr { + fn hash<H: Hasher>(&self, s: &mut H) { + let inner = self.0; + (inner.nl_family, inner.nl_pid, inner.nl_groups).hash(s); + } + } + + + impl NetlinkAddr { + pub fn new(pid: u32, groups: u32) -> NetlinkAddr { + let mut addr: sockaddr_nl = unsafe { mem::zeroed() }; + addr.nl_family = AddressFamily::Netlink as sa_family_t; + addr.nl_pid = pid; + addr.nl_groups = groups; + + NetlinkAddr(addr) + } + + pub fn pid(&self) -> u32 { + self.0.nl_pid + } + + pub fn groups(&self) -> u32 { + self.0.nl_groups + } + } + + impl fmt::Display for NetlinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "pid: {} groups: {}", self.pid(), self.groups()) + } + } + + impl fmt::Debug for NetlinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } + } +} + +#[cfg(any(target_os = "ios", target_os = "macos"))] +pub mod sys_control { + use ::sys::socket::addr::AddressFamily; + use libc::{self, c_uchar}; + use std::{fmt, mem}; + use std::hash::{Hash, Hasher}; + use std::os::unix::io::RawFd; + use {Errno, Error, Result}; + + #[repr(C)] + pub struct ctl_ioc_info { + pub ctl_id: u32, + pub ctl_name: [c_uchar; MAX_KCTL_NAME], + } + + const CTL_IOC_MAGIC: u8 = 'N' as u8; + const CTL_IOC_INFO: u8 = 3; + const MAX_KCTL_NAME: usize = 96; + + ioctl_readwrite!(ctl_info, CTL_IOC_MAGIC, CTL_IOC_INFO, ctl_ioc_info); + + #[derive(Copy, Clone)] + #[repr(C)] + pub struct SysControlAddr(pub libc::sockaddr_ctl); + + impl PartialEq for SysControlAddr { + fn eq(&self, other: &Self) -> bool { + let (inner, other) = (self.0, other.0); + (inner.sc_id, inner.sc_unit) == + (other.sc_id, other.sc_unit) + } + } + + impl Eq for SysControlAddr {} + + impl Hash for SysControlAddr { + fn hash<H: Hasher>(&self, s: &mut H) { + let inner = self.0; + (inner.sc_id, inner.sc_unit).hash(s); + } + } + + + impl SysControlAddr { + pub fn new(id: u32, unit: u32) -> SysControlAddr { + let addr = libc::sockaddr_ctl { + sc_len: mem::size_of::<libc::sockaddr_ctl>() as c_uchar, + sc_family: AddressFamily::System as c_uchar, + ss_sysaddr: libc::AF_SYS_CONTROL as u16, + sc_id: id, + sc_unit: unit, + sc_reserved: [0; 5] + }; + + SysControlAddr(addr) + } + + pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result<SysControlAddr> { + if name.len() > MAX_KCTL_NAME { + return Err(Error::Sys(Errno::ENAMETOOLONG)); + } + + let mut ctl_name = [0; MAX_KCTL_NAME]; + ctl_name[..name.len()].clone_from_slice(name.as_bytes()); + let mut info = ctl_ioc_info { ctl_id: 0, ctl_name: ctl_name }; + + unsafe { ctl_info(sockfd, &mut info)?; } + + Ok(SysControlAddr::new(info.ctl_id, unit)) + } + + pub fn id(&self) -> u32 { + self.0.sc_id + } + + pub fn unit(&self) -> u32 { + self.0.sc_unit + } + } + + impl fmt::Display for SysControlAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self, f) + } + } + + impl fmt::Debug for SysControlAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("SysControlAddr") + .field("sc_len", &self.0.sc_len) + .field("sc_family", &self.0.sc_family) + .field("ss_sysaddr", &self.0.ss_sysaddr) + .field("sc_id", &self.0.sc_id) + .field("sc_unit", &self.0.sc_unit) + .finish() + } + } +} + + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod datalink { + use super::{libc, hash, fmt, AddressFamily}; + + /// Hardware Address + #[derive(Clone, Copy)] + pub struct LinkAddr(pub libc::sockaddr_ll); + + impl LinkAddr { + /// Always AF_PACKET + pub fn family(&self) -> AddressFamily { + assert_eq!(self.0.sll_family as i32, libc::AF_PACKET); + AddressFamily::Packet + } + + /// Physical-layer protocol + pub fn protocol(&self) -> u16 { + self.0.sll_protocol + } + + /// Interface number + pub fn ifindex(&self) -> usize { + self.0.sll_ifindex as usize + } + + /// ARP hardware type + pub fn hatype(&self) -> u16 { + self.0.sll_hatype + } + + /// Packet type + pub fn pkttype(&self) -> u8 { + self.0.sll_pkttype + } + + /// Length of MAC address + pub fn halen(&self) -> usize { + self.0.sll_halen as usize + } + + /// Physical-layer address (MAC) + pub fn addr(&self) -> [u8; 6] { + let a = self.0.sll_addr[0] as u8; + let b = self.0.sll_addr[1] as u8; + let c = self.0.sll_addr[2] as u8; + let d = self.0.sll_addr[3] as u8; + let e = self.0.sll_addr[4] as u8; + let f = self.0.sll_addr[5] as u8; + + [a, b, c, d, e, f] + } + } + + impl Eq for LinkAddr {} + + impl PartialEq for LinkAddr { + fn eq(&self, other: &Self) -> bool { + let (a, b) = (self.0, other.0); + (a.sll_family, a.sll_protocol, a.sll_ifindex, a.sll_hatype, + a.sll_pkttype, a.sll_halen, a.sll_addr) == + (b.sll_family, b.sll_protocol, b.sll_ifindex, b.sll_hatype, + b.sll_pkttype, b.sll_halen, b.sll_addr) + } + } + + impl hash::Hash for LinkAddr { + fn hash<H: hash::Hasher>(&self, s: &mut H) { + let a = self.0; + (a.sll_family, a.sll_protocol, a.sll_ifindex, a.sll_hatype, + a.sll_pkttype, a.sll_halen, a.sll_addr).hash(s); + } + } + + impl fmt::Display for LinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let addr = self.addr(); + write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + addr[0], + addr[1], + addr[2], + addr[3], + addr[4], + addr[5]) + } + } + + impl fmt::Debug for LinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } + } +} + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +mod datalink { + use super::{libc, hash, fmt, AddressFamily}; + + /// Hardware Address + #[derive(Clone, Copy)] + pub struct LinkAddr(pub libc::sockaddr_dl); + + impl LinkAddr { + /// Total length of sockaddr + pub fn len(&self) -> usize { + self.0.sdl_len as usize + } + + /// always == AF_LINK + pub fn family(&self) -> AddressFamily { + assert_eq!(self.0.sdl_family as i32, libc::AF_LINK); + AddressFamily::Link + } + + /// interface index, if != 0, system given index for interface + pub fn ifindex(&self) -> usize { + self.0.sdl_index as usize + } + + /// Datalink type + pub fn datalink_type(&self) -> u8 { + self.0.sdl_type + } + + // MAC address start position + pub fn nlen(&self) -> usize { + self.0.sdl_nlen as usize + } + + /// link level address length + pub fn alen(&self) -> usize { + self.0.sdl_alen as usize + } + + /// link layer selector length + pub fn slen(&self) -> usize { + self.0.sdl_slen as usize + } + + /// if link level address length == 0, + /// or `sdl_data` not be larger. + pub fn is_empty(&self) -> bool { + let nlen = self.nlen(); + let alen = self.alen(); + let data_len = self.0.sdl_data.len(); + + if alen > 0 && nlen + alen < data_len { + false + } else { + true + } + } + + /// Physical-layer address (MAC) + pub fn addr(&self) -> [u8; 6] { + let nlen = self.nlen(); + let data = self.0.sdl_data; + + assert!(!self.is_empty()); + + let a = data[nlen] as u8; + let b = data[nlen + 1] as u8; + let c = data[nlen + 2] as u8; + let d = data[nlen + 3] as u8; + let e = data[nlen + 4] as u8; + let f = data[nlen + 5] as u8; + + [a, b, c, d, e, f] + } + } + + impl Eq for LinkAddr {} + + impl PartialEq for LinkAddr { + #[cfg(any(target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + fn eq(&self, other: &Self) -> bool { + let (a, b) = (self.0, other.0); + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, + a.sdl_nlen, a.sdl_alen, a.sdl_slen, &a.sdl_data[..]) == + (b.sdl_len, b.sdl_family, b.sdl_index, b.sdl_type, + b.sdl_nlen, b.sdl_alen, b.sdl_slen, &b.sdl_data[..]) + } + + #[cfg(target_os = "dragonfly")] + fn eq(&self, other: &Self) -> bool { + let (a, b) = (self.0, other.0); + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, a.sdl_nlen, + a.sdl_alen, a.sdl_slen, a.sdl_data, a.sdl_rcf, a.sdl_route) == + (b.sdl_len, b.sdl_family, b.sdl_index, b.sdl_type, b.sdl_nlen, + b.sdl_alen, b.sdl_slen, b.sdl_data, b.sdl_rcf, b.sdl_route) + } + } + + impl hash::Hash for LinkAddr { + #[cfg(any(target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + fn hash<H: hash::Hasher>(&self, s: &mut H) { + let a = self.0; + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, + a.sdl_nlen, a.sdl_alen, a.sdl_slen, &a.sdl_data[..]).hash(s); + } + + #[cfg(target_os = "dragonfly")] + fn hash<H: hash::Hasher>(&self, s: &mut H) { + let a = self.0; + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, a.sdl_nlen, + a.sdl_alen, a.sdl_slen, a.sdl_data, a.sdl_rcf, a.sdl_route).hash(s); + } + } + + impl fmt::Display for LinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let addr = self.addr(); + write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + addr[0], + addr[1], + addr[2], + addr[3], + addr[4], + addr[5]) + } + } + + impl fmt::Debug for LinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } + } +} + +#[cfg(test)] +mod tests { + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + use super::*; + + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + #[test] + fn test_macos_loopback_datalink_addr() { + let bytes = [20i8, 18, 1, 0, 24, 3, 0, 0, 108, 111, 48, 0, 0, 0, 0, 0]; + let sa = bytes.as_ptr() as *const libc::sockaddr; + let _sock_addr = unsafe { SockAddr::from_libc_sockaddr(sa) }; + assert!(_sock_addr.is_none()); + } + + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + #[test] + fn test_macos_tap_datalink_addr() { + let bytes = [20i8, 18, 7, 0, 6, 3, 6, 0, 101, 110, 48, 24, 101, -112, -35, 76, -80]; + let ptr = bytes.as_ptr(); + let sa = ptr as *const libc::sockaddr; + let _sock_addr = unsafe { SockAddr::from_libc_sockaddr(sa) }; + + assert!(_sock_addr.is_some()); + + let sock_addr = _sock_addr.unwrap(); + + assert_eq!(sock_addr.family(), AddressFamily::Link); + + match sock_addr { + SockAddr::Link(ether_addr) => { + assert_eq!(ether_addr.addr(), [24u8, 101, 144, 221, 76, 176]); + }, + _ => { unreachable!() } + }; + } +} diff --git a/third_party/rust/nix/src/sys/socket/mod.rs b/third_party/rust/nix/src/sys/socket/mod.rs new file mode 100644 index 0000000000..62c0ea7412 --- /dev/null +++ b/third_party/rust/nix/src/sys/socket/mod.rs @@ -0,0 +1,1204 @@ +//! Socket interface functions +//! +//! [Further reading](http://man7.org/linux/man-pages/man7/socket.7.html) +use {Error, Result}; +use errno::Errno; +use libc::{self, c_void, c_int, socklen_t, size_t}; +use std::{fmt, mem, ptr, slice}; +use std::os::unix::io::RawFd; +use sys::time::TimeVal; +use sys::uio::IoVec; + +mod addr; +pub mod sockopt; + +/* + * + * ===== Re-exports ===== + * + */ + +pub use self::addr::{ + AddressFamily, + SockAddr, + InetAddr, + UnixAddr, + IpAddr, + Ipv4Addr, + Ipv6Addr, + LinkAddr, +}; +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use ::sys::socket::addr::netlink::NetlinkAddr; + +pub use libc::{ + cmsghdr, + msghdr, + sa_family_t, + sockaddr, + sockaddr_in, + sockaddr_in6, + sockaddr_storage, + sockaddr_un, +}; + +/// These constants are used to specify the communication semantics +/// when creating a socket with [`socket()`](fn.socket.html) +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[repr(i32)] +pub enum SockType { + /// Provides sequenced, reliable, two-way, connection- + /// based byte streams. An out-of-band data transmission + /// mechanism may be supported. + Stream = libc::SOCK_STREAM, + /// Supports datagrams (connectionless, unreliable + /// messages of a fixed maximum length). + Datagram = libc::SOCK_DGRAM, + /// Provides a sequenced, reliable, two-way connection- + /// based data transmission path for datagrams of fixed + /// maximum length; a consumer is required to read an + /// entire packet with each input system call. + SeqPacket = libc::SOCK_SEQPACKET, + /// Provides raw network protocol access. + Raw = libc::SOCK_RAW, + /// Provides a reliable datagram layer that does not + /// guarantee ordering. + Rdm = libc::SOCK_RDM, +} + +/// Constants used in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html) +/// to specify the protocol to use. +#[repr(i32)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SockProtocol { + /// TCP protocol ([ip(7)](http://man7.org/linux/man-pages/man7/ip.7.html)) + Tcp = libc::IPPROTO_TCP, + /// UDP protocol ([ip(7)](http://man7.org/linux/man-pages/man7/ip.7.html)) + Udp = libc::IPPROTO_UDP, + /// Allows applications and other KEXTs to be notified when certain kernel events occur + /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html)) + #[cfg(any(target_os = "ios", target_os = "macos"))] + KextEvent = libc::SYSPROTO_EVENT, + /// Allows applications to configure and control a KEXT + /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html)) + #[cfg(any(target_os = "ios", target_os = "macos"))] + KextControl = libc::SYSPROTO_CONTROL, +} + +libc_bitflags!{ + /// Additional socket options + pub struct SockFlag: c_int { + /// Set non-blocking mode on the new socket + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] + SOCK_NONBLOCK; + /// Set close-on-exec on the new descriptor + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] + SOCK_CLOEXEC; + /// Return `EPIPE` instead of raising `SIGPIPE` + #[cfg(target_os = "netbsd")] + SOCK_NOSIGPIPE; + /// For domains `AF_INET(6)`, only allow `connect(2)`, `sendto(2)`, or `sendmsg(2)` + /// to the DNS port (typically 53) + #[cfg(target_os = "openbsd")] + SOCK_DNS; + } +} + +libc_bitflags!{ + /// Flags for send/recv and their relatives + pub struct MsgFlags: c_int { + /// Sends or requests out-of-band data on sockets that support this notion + /// (e.g., of type [`Stream`](enum.SockType.html)); the underlying protocol must also + /// support out-of-band data. + MSG_OOB; + /// Peeks at an incoming message. The data is treated as unread and the next + /// [`recv()`](fn.recv.html) + /// or similar function shall still return this data. + MSG_PEEK; + /// Enables nonblocking operation; if the operation would block, + /// `EAGAIN` or `EWOULDBLOCK` is returned. This provides similar + /// behavior to setting the `O_NONBLOCK` flag + /// (via the [`fcntl`](../../fcntl/fn.fcntl.html) + /// `F_SETFL` operation), but differs in that `MSG_DONTWAIT` is a per- + /// call option, whereas `O_NONBLOCK` is a setting on the open file + /// description (see [open(2)](http://man7.org/linux/man-pages/man2/open.2.html)), + /// which will affect all threads in + /// the calling process and as well as other processes that hold + /// file descriptors referring to the same open file description. + MSG_DONTWAIT; + /// Receive flags: Control Data was discarded (buffer too small) + MSG_CTRUNC; + /// For raw ([`Packet`](addr/enum.AddressFamily.html)), Internet datagram + /// (since Linux 2.4.27/2.6.8), + /// netlink (since Linux 2.6.22) and UNIX datagram (since Linux 3.4) + /// sockets: return the real length of the packet or datagram, even + /// when it was longer than the passed buffer. Not implemented for UNIX + /// domain ([unix(7)](https://linux.die.net/man/7/unix)) sockets. + /// + /// For use with Internet stream sockets, see [tcp(7)](https://linux.die.net/man/7/tcp). + MSG_TRUNC; + /// Terminates a record (when this notion is supported, as for + /// sockets of type [`SeqPacket`](enum.SockType.html)). + MSG_EOR; + /// This flag specifies that queued errors should be received from + /// the socket error queue. (For more details, see + /// [recvfrom(2)](https://linux.die.net/man/2/recvfrom)) + #[cfg(any(target_os = "android", target_os = "linux"))] + MSG_ERRQUEUE; + /// Set the `close-on-exec` flag for the file descriptor received via a UNIX domain + /// file descriptor using the `SCM_RIGHTS` operation (described in + /// [unix(7)](https://linux.die.net/man/7/unix)). + /// This flag is useful for the same reasons as the `O_CLOEXEC` flag of + /// [open(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html). + /// + /// Only used in [`recvmsg`](fn.recvmsg.html) function. + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] + MSG_CMSG_CLOEXEC; + } +} + +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + /// Unix credentials of the sending process. + /// + /// This struct is used with the `SO_PEERCRED` ancillary message for UNIX sockets. + #[repr(C)] + #[derive(Clone, Copy)] + pub struct UnixCredentials(libc::ucred); + + impl UnixCredentials { + /// Returns the process identifier + pub fn pid(&self) -> libc::pid_t { + self.0.pid + } + + /// Returns the user identifier + pub fn uid(&self) -> libc::uid_t { + self.0.uid + } + + /// Returns the group identifier + pub fn gid(&self) -> libc::gid_t { + self.0.gid + } + } + + impl PartialEq for UnixCredentials { + fn eq(&self, other: &Self) -> bool { + self.0.pid == other.0.pid && self.0.uid == other.0.uid && self.0.gid == other.0.gid + } + } + impl Eq for UnixCredentials {} + + impl From<libc::ucred> for UnixCredentials { + fn from(cred: libc::ucred) -> Self { + UnixCredentials(cred) + } + } + + impl Into<libc::ucred> for UnixCredentials { + fn into(self) -> libc::ucred { + self.0 + } + } + + impl fmt::Debug for UnixCredentials { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("UnixCredentials") + .field("pid", &self.0.pid) + .field("uid", &self.0.uid) + .field("gid", &self.0.gid) + .finish() + } + } + } +} + +/// Request for multicast socket operations +/// +/// This is a wrapper type around `ip_mreq`. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IpMembershipRequest(libc::ip_mreq); + +impl IpMembershipRequest { + /// Instantiate a new `IpMembershipRequest` + /// + /// If `interface` is `None`, then `Ipv4Addr::any()` will be used for the interface. + pub fn new(group: Ipv4Addr, interface: Option<Ipv4Addr>) -> Self { + IpMembershipRequest(libc::ip_mreq { + imr_multiaddr: group.0, + imr_interface: interface.unwrap_or_else(Ipv4Addr::any).0, + }) + } +} + +impl PartialEq for IpMembershipRequest { + fn eq(&self, other: &Self) -> bool { + self.0.imr_multiaddr.s_addr == other.0.imr_multiaddr.s_addr + && self.0.imr_interface.s_addr == other.0.imr_interface.s_addr + } +} +impl Eq for IpMembershipRequest {} + +impl fmt::Debug for IpMembershipRequest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mref = &self.0.imr_multiaddr; + let maddr = mref.s_addr; + let iref = &self.0.imr_interface; + let ifaddr = iref.s_addr; + f.debug_struct("IpMembershipRequest") + .field("imr_multiaddr", &maddr) + .field("imr_interface", &ifaddr) + .finish() + } +} + +/// Request for ipv6 multicast socket operations +/// +/// This is a wrapper type around `ipv6_mreq`. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct Ipv6MembershipRequest(libc::ipv6_mreq); + +impl Ipv6MembershipRequest { + /// Instantiate a new `Ipv6MembershipRequest` + pub fn new(group: Ipv6Addr) -> Self { + Ipv6MembershipRequest(libc::ipv6_mreq { + ipv6mr_multiaddr: group.0, + ipv6mr_interface: 0, + }) + } +} + +impl PartialEq for Ipv6MembershipRequest { + fn eq(&self, other: &Self) -> bool { + self.0.ipv6mr_multiaddr.s6_addr == other.0.ipv6mr_multiaddr.s6_addr && + self.0.ipv6mr_interface == other.0.ipv6mr_interface + } +} +impl Eq for Ipv6MembershipRequest {} + +impl fmt::Debug for Ipv6MembershipRequest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Ipv6MembershipRequest") + .field("ipv6mr_multiaddr", &self.0.ipv6mr_multiaddr.s6_addr) + .field("ipv6mr_interface", &self.0.ipv6mr_interface) + .finish() + } +} + +/// Copy the in-memory representation of `src` into the byte slice `dst`. +/// +/// Returns the remainder of `dst`. +/// +/// Panics when `dst` is too small for `src` (more precisely, panics if +/// `mem::size_of_val(src) >= dst.len()`). +/// +/// Unsafe because it transmutes `src` to raw bytes, which is only safe for some +/// types `T`. Refer to the [Rustonomicon] for details. +/// +/// [Rustonomicon]: https://doc.rust-lang.org/nomicon/transmutes.html +unsafe fn copy_bytes<'a, T: ?Sized>(src: &T, dst: &'a mut [u8]) -> &'a mut [u8] { + let srclen = mem::size_of_val(src); + ptr::copy_nonoverlapping( + src as *const T as *const u8, + dst[..srclen].as_mut_ptr(), + srclen + ); + + &mut dst[srclen..] +} + +/// Fills `dst` with `len` zero bytes and returns the remainder of the slice. +/// +/// Panics when `len >= dst.len()`. +fn pad_bytes(len: usize, dst: &mut [u8]) -> &mut [u8] { + for pad in &mut dst[..len] { + *pad = 0; + } + + &mut dst[len..] +} + +cfg_if! { + // Darwin and DragonFly BSD always align struct cmsghdr to 32-bit only. + if #[cfg(any(target_os = "dragonfly", target_os = "ios", target_os = "macos"))] { + type align_of_cmsg_data = u32; + } else { + type align_of_cmsg_data = size_t; + } +} + +/// A structure used to make room in a cmsghdr passed to recvmsg. The +/// size and alignment match that of a cmsghdr followed by a T, but the +/// fields are not accessible, as the actual types will change on a call +/// to recvmsg. +/// +/// To make room for multiple messages, nest the type parameter with +/// tuples: +/// +/// ``` +/// use std::os::unix::io::RawFd; +/// use nix::sys::socket::CmsgSpace; +/// let cmsg: CmsgSpace<([RawFd; 3], CmsgSpace<[RawFd; 2]>)> = CmsgSpace::new(); +/// ``` +#[repr(C)] +#[allow(missing_debug_implementations)] +pub struct CmsgSpace<T> { + _hdr: cmsghdr, + _pad: [align_of_cmsg_data; 0], + _data: T, +} + +impl<T> CmsgSpace<T> { + /// Create a CmsgSpace<T>. The structure is used only for space, so + /// the fields are uninitialized. + pub fn new() -> Self { + // Safe because the fields themselves aren't accessible. + unsafe { mem::uninitialized() } + } +} + +#[derive(Debug)] +pub struct RecvMsg<'a> { + // The number of bytes received. + pub bytes: usize, + cmsg_buffer: &'a [u8], + pub address: Option<SockAddr>, + pub flags: MsgFlags, +} + +impl<'a> RecvMsg<'a> { + /// Iterate over the valid control messages pointed to by this + /// msghdr. + pub fn cmsgs(&self) -> CmsgIterator { + CmsgIterator { + buf: self.cmsg_buffer, + } + } +} + +#[derive(Debug)] +pub struct CmsgIterator<'a> { + /// Control message buffer to decode from. Must adhere to cmsg alignment. + buf: &'a [u8], +} + +impl<'a> Iterator for CmsgIterator<'a> { + type Item = ControlMessage<'a>; + + // The implementation loosely follows CMSG_FIRSTHDR / CMSG_NXTHDR, + // although we handle the invariants in slightly different places to + // get a better iterator interface. + fn next(&mut self) -> Option<ControlMessage<'a>> { + if self.buf.len() == 0 { + // The iterator assumes that `self.buf` always contains exactly the + // bytes we need, so we're at the end when the buffer is empty. + return None; + } + + // Safe if: `self.buf` is `cmsghdr`-aligned. + let cmsg: &'a cmsghdr = unsafe { + &*(self.buf[..mem::size_of::<cmsghdr>()].as_ptr() as *const cmsghdr) + }; + + let cmsg_len = cmsg.cmsg_len as usize; + + // Advance our internal pointer. + let cmsg_data = &self.buf[cmsg_align(mem::size_of::<cmsghdr>())..cmsg_len]; + self.buf = &self.buf[cmsg_align(cmsg_len)..]; + + // Safe if: `cmsg_data` contains the expected (amount of) content. This + // is verified by the kernel. + unsafe { + Some(ControlMessage::decode_from(cmsg, cmsg_data)) + } + } +} + +/// A type-safe wrapper around a single control message. More types may +/// be added to this enum; do not exhaustively pattern-match it. +/// [Further reading](http://man7.org/linux/man-pages/man3/cmsg.3.html) +#[allow(missing_debug_implementations)] +pub enum ControlMessage<'a> { + /// A message of type `SCM_RIGHTS`, containing an array of file + /// descriptors passed between processes. + /// + /// See the description in the "Ancillary messages" section of the + /// [unix(7) man page](http://man7.org/linux/man-pages/man7/unix.7.html). + /// + /// Using multiple `ScmRights` messages for a single `sendmsg` call isn't recommended since it + /// causes platform-dependent behaviour: It might swallow all but the first `ScmRights` message + /// or fail with `EINVAL`. Instead, you can put all fds to be passed into a single `ScmRights` + /// message. + ScmRights(&'a [RawFd]), + /// A message of type `SCM_CREDENTIALS`, containing the pid, uid and gid of + /// a process connected to the socket. + /// + /// This is similar to the socket option `SO_PEERCRED`, but requires a + /// process to explicitly send its credentials. A process running as root is + /// allowed to specify any credentials, while credentials sent by other + /// processes are verified by the kernel. + /// + /// For further information, please refer to the + /// [`unix(7)`](http://man7.org/linux/man-pages/man7/unix.7.html) man page. + // FIXME: When `#[repr(transparent)]` is stable, use it on `UnixCredentials` + // and put that in here instead of a raw ucred. + #[cfg(any(target_os = "android", target_os = "linux"))] + ScmCredentials(&'a libc::ucred), + /// A message of type `SCM_TIMESTAMP`, containing the time the + /// packet was received by the kernel. + /// + /// See the kernel's explanation in "SO_TIMESTAMP" of + /// [networking/timestamping](https://www.kernel.org/doc/Documentation/networking/timestamping.txt). + /// + /// # Examples + /// + // Disable this test on FreeBSD i386 + // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=222039 + #[cfg_attr(not(all(target_os = "freebsd", target_arch = "x86")), doc = " ```")] + #[cfg_attr(all(target_os = "freebsd", target_arch = "x86"), doc = " ```no_run")] + /// use nix::sys::socket::*; + /// use nix::sys::uio::IoVec; + /// use nix::sys::time::*; + /// use std::time::*; + /// + /// // Set up + /// let message = "Ohayō!".as_bytes(); + /// let in_socket = socket( + /// AddressFamily::Inet, + /// SockType::Datagram, + /// SockFlag::empty(), + /// None).unwrap(); + /// setsockopt(in_socket, sockopt::ReceiveTimestamp, &true).unwrap(); + /// let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0); + /// bind(in_socket, &SockAddr::new_inet(localhost)).unwrap(); + /// let address = getsockname(in_socket).unwrap(); + /// // Get initial time + /// let time0 = SystemTime::now(); + /// // Send the message + /// let iov = [IoVec::from_slice(message)]; + /// let flags = MsgFlags::empty(); + /// let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap(); + /// assert_eq!(message.len(), l); + /// // Receive the message + /// let mut buffer = vec![0u8; message.len()]; + /// let mut cmsgspace: CmsgSpace<TimeVal> = CmsgSpace::new(); + /// let iov = [IoVec::from_mut_slice(&mut buffer)]; + /// let r = recvmsg(in_socket, &iov, Some(&mut cmsgspace), flags).unwrap(); + /// let rtime = match r.cmsgs().next() { + /// Some(ControlMessage::ScmTimestamp(&rtime)) => rtime, + /// Some(_) => panic!("Unexpected control message"), + /// None => panic!("No control message") + /// }; + /// // Check the final time + /// let time1 = SystemTime::now(); + /// // the packet's received timestamp should lie in-between the two system + /// // times, unless the system clock was adjusted in the meantime. + /// let rduration = Duration::new(rtime.tv_sec() as u64, + /// rtime.tv_usec() as u32 * 1000); + /// assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration); + /// assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap()); + /// // Close socket + /// nix::unistd::close(in_socket).unwrap(); + /// ``` + ScmTimestamp(&'a TimeVal), + + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + Ipv4PacketInfo(&'a libc::in_pktinfo), + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + Ipv6PacketInfo(&'a libc::in6_pktinfo), + + /// Catch-all variant for unimplemented cmsg types. + #[doc(hidden)] + Unknown(UnknownCmsg<'a>), +} + +// An opaque structure used to prevent cmsghdr from being a public type +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct UnknownCmsg<'a>(&'a cmsghdr, &'a [u8]); + +// Round `len` up to meet the platform's required alignment for +// `cmsghdr`s and trailing `cmsghdr` data. This should match the +// behaviour of CMSG_ALIGN from the Linux headers and do the correct +// thing on other platforms that don't usually provide CMSG_ALIGN. +#[inline] +fn cmsg_align(len: usize) -> usize { + let align_bytes = mem::size_of::<align_of_cmsg_data>() - 1; + (len + align_bytes) & !align_bytes +} + +impl<'a> ControlMessage<'a> { + /// The value of CMSG_SPACE on this message. + fn space(&self) -> usize { + cmsg_align(self.len()) + } + + /// The value of CMSG_LEN on this message. + fn len(&self) -> usize { + cmsg_align(mem::size_of::<cmsghdr>()) + match *self { + ControlMessage::ScmRights(fds) => { + mem::size_of_val(fds) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::ScmCredentials(creds) => { + mem::size_of_val(creds) + } + ControlMessage::ScmTimestamp(t) => { + mem::size_of_val(t) + }, + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv4PacketInfo(pktinfo) => { + mem::size_of_val(pktinfo) + }, + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv6PacketInfo(pktinfo) => { + mem::size_of_val(pktinfo) + }, + ControlMessage::Unknown(UnknownCmsg(_, bytes)) => { + mem::size_of_val(bytes) + } + } + } + + /// Returns the value to put into the `cmsg_level` field of the header. + fn cmsg_level(&self) -> libc::c_int { + match *self { + ControlMessage::ScmRights(_) => libc::SOL_SOCKET, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET, + ControlMessage::ScmTimestamp(_) => libc::SOL_SOCKET, + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv4PacketInfo(_) => libc::IPPROTO_IP, + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6, + ControlMessage::Unknown(ref cmsg) => cmsg.0.cmsg_level, + } + } + + /// Returns the value to put into the `cmsg_type` field of the header. + fn cmsg_type(&self) -> libc::c_int { + match *self { + ControlMessage::ScmRights(_) => libc::SCM_RIGHTS, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS, + ControlMessage::ScmTimestamp(_) => libc::SCM_TIMESTAMP, + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv4PacketInfo(_) => libc::IP_PKTINFO, + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO, + ControlMessage::Unknown(ref cmsg) => cmsg.0.cmsg_type, + } + } + + // Unsafe: start and end of buffer must be cmsg_align'd. Updates + // the provided slice; panics if the buffer is too small. + unsafe fn encode_into(&self, buf: &mut [u8]) { + let final_buf = if let ControlMessage::Unknown(ref cmsg) = *self { + let &UnknownCmsg(orig_cmsg, bytes) = cmsg; + + let buf = copy_bytes(orig_cmsg, buf); + + let padlen = cmsg_align(mem::size_of_val(&orig_cmsg)) - + mem::size_of_val(&orig_cmsg); + let buf = pad_bytes(padlen, buf); + + copy_bytes(bytes, buf) + } else { + let cmsg = cmsghdr { + cmsg_len: self.len() as _, + cmsg_level: self.cmsg_level(), + cmsg_type: self.cmsg_type(), + ..mem::zeroed() // zero out platform-dependent padding fields + }; + let buf = copy_bytes(&cmsg, buf); + + let padlen = cmsg_align(mem::size_of_val(&cmsg)) - + mem::size_of_val(&cmsg); + let buf = pad_bytes(padlen, buf); + + match *self { + ControlMessage::ScmRights(fds) => { + copy_bytes(fds, buf) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::ScmCredentials(creds) => { + copy_bytes(creds, buf) + }, + ControlMessage::ScmTimestamp(t) => { + copy_bytes(t, buf) + }, + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv4PacketInfo(pktinfo) => { + copy_bytes(pktinfo, buf) + }, + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv6PacketInfo(pktinfo) => { + copy_bytes(pktinfo, buf) + } + ControlMessage::Unknown(_) => unreachable!(), + } + }; + + let padlen = self.space() - self.len(); + pad_bytes(padlen, final_buf); + } + + /// Decodes a `ControlMessage` from raw bytes. + /// + /// This is only safe to call if the data is correct for the message type + /// specified in the header. Normally, the kernel ensures that this is the + /// case. "Correct" in this case includes correct length, alignment and + /// actual content. + unsafe fn decode_from(header: &'a cmsghdr, data: &'a [u8]) -> ControlMessage<'a> { + match (header.cmsg_level, header.cmsg_type) { + (libc::SOL_SOCKET, libc::SCM_RIGHTS) => { + ControlMessage::ScmRights( + slice::from_raw_parts(data.as_ptr() as *const _, + data.len() / mem::size_of::<RawFd>())) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + (libc::SOL_SOCKET, libc::SCM_CREDENTIALS) => { + ControlMessage::ScmCredentials( + &*(data.as_ptr() as *const _) + ) + } + (libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => { + ControlMessage::ScmTimestamp( + &*(data.as_ptr() as *const _)) + }, + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => { + ControlMessage::Ipv6PacketInfo( + &*(data.as_ptr() as *const _)) + } + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + (libc::IPPROTO_IP, libc::IP_PKTINFO) => { + ControlMessage::Ipv4PacketInfo( + &*(data.as_ptr() as *const _)) + } + + (_, _) => { + ControlMessage::Unknown(UnknownCmsg(header, data)) + } + } + } +} + + +/// Send data in scatter-gather vectors to a socket, possibly accompanied +/// by ancillary data. Optionally direct the message at the given address, +/// as with sendto. +/// +/// Allocates if cmsgs is nonempty. +pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<'a>], flags: MsgFlags, addr: Option<&'a SockAddr>) -> Result<usize> { + let mut capacity = 0; + for cmsg in cmsgs { + capacity += cmsg.space(); + } + // Note that the resulting vector claims to have length == capacity, + // so it's presently uninitialized. + let mut cmsg_buffer = unsafe { + let mut vec = Vec::<u8>::with_capacity(capacity); + vec.set_len(capacity); + vec + }; + { + let mut ofs = 0; + for cmsg in cmsgs { + let ptr = &mut cmsg_buffer[ofs..]; + unsafe { + cmsg.encode_into(ptr); + } + ofs += cmsg.space(); + } + } + + let (name, namelen) = match addr { + Some(addr) => { let (x, y) = unsafe { addr.as_ffi_pair() }; (x as *const _, y) } + None => (ptr::null(), 0), + }; + + let cmsg_ptr = if capacity > 0 { + cmsg_buffer.as_ptr() as *const c_void + } else { + ptr::null() + }; + + let mhdr = unsafe { + let mut mhdr: msghdr = mem::uninitialized(); + mhdr.msg_name = name as *mut _; + mhdr.msg_namelen = namelen; + mhdr.msg_iov = iov.as_ptr() as *mut _; + mhdr.msg_iovlen = iov.len() as _; + mhdr.msg_control = cmsg_ptr as *mut _; + mhdr.msg_controllen = capacity as _; + mhdr.msg_flags = 0; + mhdr + }; + let ret = unsafe { libc::sendmsg(fd, &mhdr, flags.bits()) }; + + Errno::result(ret).map(|r| r as usize) +} + +/// Receive message in scatter-gather vectors from a socket, and +/// optionally receive ancillary data into the provided buffer. +/// If no ancillary data is desired, use () as the type parameter. +pub fn recvmsg<'a, T>(fd: RawFd, iov: &[IoVec<&mut [u8]>], cmsg_buffer: Option<&'a mut CmsgSpace<T>>, flags: MsgFlags) -> Result<RecvMsg<'a>> { + let mut address: sockaddr_storage = unsafe { mem::uninitialized() }; + let (msg_control, msg_controllen) = match cmsg_buffer { + Some(cmsg_buffer) => (cmsg_buffer as *mut _, mem::size_of_val(cmsg_buffer)), + None => (ptr::null_mut(), 0), + }; + let mut mhdr = unsafe { + let mut mhdr: msghdr = mem::uninitialized(); + mhdr.msg_name = &mut address as *mut _ as *mut _; + mhdr.msg_namelen = mem::size_of::<sockaddr_storage>() as socklen_t; + mhdr.msg_iov = iov.as_ptr() as *mut _; + mhdr.msg_iovlen = iov.len() as _; + mhdr.msg_control = msg_control as *mut _; + mhdr.msg_controllen = msg_controllen as _; + mhdr.msg_flags = 0; + mhdr + }; + let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) }; + + let cmsg_buffer = if msg_controllen > 0 { + // got control message(s) + debug_assert!(!mhdr.msg_control.is_null()); + unsafe { + // Safe: The pointer is not null and the length is correct as part of `recvmsg`s + // contract. + slice::from_raw_parts(mhdr.msg_control as *const u8, + mhdr.msg_controllen as usize) + } + } else { + // No control message, create an empty buffer to avoid creating a slice from a null pointer + &[] + }; + + Ok(unsafe { RecvMsg { + bytes: Errno::result(ret)? as usize, + cmsg_buffer, + address: sockaddr_storage_to_addr(&address, + mhdr.msg_namelen as usize).ok(), + flags: MsgFlags::from_bits_truncate(mhdr.msg_flags), + } }) +} + + +/// Create an endpoint for communication +/// +/// The `protocol` specifies a particular protocol to be used with the +/// socket. Normally only a single protocol exists to support a +/// particular socket type within a given protocol family, in which case +/// protocol can be specified as `None`. However, it is possible that many +/// protocols may exist, in which case a particular protocol must be +/// specified in this manner. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html) +pub fn socket<T: Into<Option<SockProtocol>>>(domain: AddressFamily, ty: SockType, flags: SockFlag, protocol: T) -> Result<RawFd> { + let protocol = match protocol.into() { + None => 0, + Some(p) => p as c_int, + }; + + // SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a + // little easier to understand by separating it out. So we have to merge these bitfields + // here. + let mut ty = ty as c_int; + ty |= flags.bits(); + + let res = unsafe { libc::socket(domain as c_int, ty, protocol) }; + + Errno::result(res) +} + +/// Create a pair of connected sockets +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html) +pub fn socketpair<T: Into<Option<SockProtocol>>>(domain: AddressFamily, ty: SockType, protocol: T, + flags: SockFlag) -> Result<(RawFd, RawFd)> { + let protocol = match protocol.into() { + None => 0, + Some(p) => p as c_int, + }; + + // SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a + // little easier to understand by separating it out. So we have to merge these bitfields + // here. + let mut ty = ty as c_int; + ty |= flags.bits(); + + let mut fds = [-1, -1]; + + let res = unsafe { libc::socketpair(domain as c_int, ty, protocol, fds.as_mut_ptr()) }; + Errno::result(res)?; + + Ok((fds[0], fds[1])) +} + +/// Listen for connections on a socket +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html) +pub fn listen(sockfd: RawFd, backlog: usize) -> Result<()> { + let res = unsafe { libc::listen(sockfd, backlog as c_int) }; + + Errno::result(res).map(drop) +} + +/// Bind a name to a socket +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html) +pub fn bind(fd: RawFd, addr: &SockAddr) -> Result<()> { + let res = unsafe { + let (ptr, len) = addr.as_ffi_pair(); + libc::bind(fd, ptr, len) + }; + + Errno::result(res).map(drop) +} + +/// Accept a connection on a socket +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html) +pub fn accept(sockfd: RawFd) -> Result<RawFd> { + let res = unsafe { libc::accept(sockfd, ptr::null_mut(), ptr::null_mut()) }; + + Errno::result(res) +} + +/// Accept a connection on a socket +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/accept.2.html) +#[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "linux", + target_os = "openbsd"))] +pub fn accept4(sockfd: RawFd, flags: SockFlag) -> Result<RawFd> { + let res = unsafe { libc::accept4(sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits()) }; + + Errno::result(res) +} + +/// Initiate a connection on a socket +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html) +pub fn connect(fd: RawFd, addr: &SockAddr) -> Result<()> { + let res = unsafe { + let (ptr, len) = addr.as_ffi_pair(); + libc::connect(fd, ptr, len) + }; + + Errno::result(res).map(drop) +} + +/// Receive data from a connection-oriented socket. Returns the number of +/// bytes read +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html) +pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: MsgFlags) -> Result<usize> { + unsafe { + let ret = libc::recv( + sockfd, + buf.as_ptr() as *mut c_void, + buf.len() as size_t, + flags.bits()); + + Errno::result(ret).map(|r| r as usize) + } +} + +/// Receive data from a connectionless or connection-oriented socket. Returns +/// the number of bytes read and the socket address of the sender. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html) +pub fn recvfrom(sockfd: RawFd, buf: &mut [u8]) -> Result<(usize, SockAddr)> { + unsafe { + let addr: sockaddr_storage = mem::zeroed(); + let mut len = mem::size_of::<sockaddr_storage>() as socklen_t; + + let ret = Errno::result(libc::recvfrom( + sockfd, + buf.as_ptr() as *mut c_void, + buf.len() as size_t, + 0, + mem::transmute(&addr), + &mut len as *mut socklen_t))?; + + sockaddr_storage_to_addr(&addr, len as usize) + .map(|addr| (ret as usize, addr)) + } +} + +/// Send a message to a socket +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html) +pub fn sendto(fd: RawFd, buf: &[u8], addr: &SockAddr, flags: MsgFlags) -> Result<usize> { + let ret = unsafe { + let (ptr, len) = addr.as_ffi_pair(); + libc::sendto(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags.bits(), ptr, len) + }; + + Errno::result(ret).map(|r| r as usize) +} + +/// Send data to a connection-oriented socket. Returns the number of bytes read +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html) +pub fn send(fd: RawFd, buf: &[u8], flags: MsgFlags) -> Result<usize> { + let ret = unsafe { + libc::send(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags.bits()) + }; + + Errno::result(ret).map(|r| r as usize) +} + +/* + * + * ===== Socket Options ===== + * + */ + +/// The protocol level at which to get / set socket options. Used as an +/// argument to `getsockopt` and `setsockopt`. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html) +#[repr(i32)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SockLevel { + Socket = libc::SOL_SOCKET, + Tcp = libc::IPPROTO_TCP, + Ip = libc::IPPROTO_IP, + Ipv6 = libc::IPPROTO_IPV6, + Udp = libc::IPPROTO_UDP, + #[cfg(any(target_os = "android", target_os = "linux"))] + Netlink = libc::SOL_NETLINK, +} + +/// Represents a socket option that can be accessed or set. Used as an argument +/// to `getsockopt` +pub trait GetSockOpt : Copy { + type Val; + + #[doc(hidden)] + fn get(&self, fd: RawFd) -> Result<Self::Val>; +} + +/// Represents a socket option that can be accessed or set. Used as an argument +/// to `setsockopt` +pub trait SetSockOpt : Copy { + type Val; + + #[doc(hidden)] + fn set(&self, fd: RawFd, val: &Self::Val) -> Result<()>; +} + +/// Get the current value for the requested socket option +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html) +pub fn getsockopt<O: GetSockOpt>(fd: RawFd, opt: O) -> Result<O::Val> { + opt.get(fd) +} + +/// Sets the value for the requested socket option +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html) +/// +/// # Examples +/// +/// ``` +/// use nix::sys::socket::setsockopt; +/// use nix::sys::socket::sockopt::KeepAlive; +/// use std::net::TcpListener; +/// use std::os::unix::io::AsRawFd; +/// +/// let listener = TcpListener::bind("0.0.0.0:0").unwrap(); +/// let fd = listener.as_raw_fd(); +/// let res = setsockopt(fd, KeepAlive, &true); +/// assert!(res.is_ok()); +/// ``` +pub fn setsockopt<O: SetSockOpt>(fd: RawFd, opt: O, val: &O::Val) -> Result<()> { + opt.set(fd, val) +} + +/// Get the address of the peer connected to the socket `fd`. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html) +pub fn getpeername(fd: RawFd) -> Result<SockAddr> { + unsafe { + let addr: sockaddr_storage = mem::uninitialized(); + let mut len = mem::size_of::<sockaddr_storage>() as socklen_t; + + let ret = libc::getpeername(fd, mem::transmute(&addr), &mut len); + + Errno::result(ret)?; + + sockaddr_storage_to_addr(&addr, len as usize) + } +} + +/// Get the current address to which the socket `fd` is bound. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html) +pub fn getsockname(fd: RawFd) -> Result<SockAddr> { + unsafe { + let addr: sockaddr_storage = mem::uninitialized(); + let mut len = mem::size_of::<sockaddr_storage>() as socklen_t; + + let ret = libc::getsockname(fd, mem::transmute(&addr), &mut len); + + Errno::result(ret)?; + + sockaddr_storage_to_addr(&addr, len as usize) + } +} + +/// Return the appropriate `SockAddr` type from a `sockaddr_storage` of a certain +/// size. In C this would usually be done by casting. The `len` argument +/// should be the number of bytes in the `sockaddr_storage` that are actually +/// allocated and valid. It must be at least as large as all the useful parts +/// of the structure. Note that in the case of a `sockaddr_un`, `len` need not +/// include the terminating null. +pub unsafe fn sockaddr_storage_to_addr( + addr: &sockaddr_storage, + len: usize) -> Result<SockAddr> { + + if len < mem::size_of_val(&addr.ss_family) { + return Err(Error::Sys(Errno::ENOTCONN)); + } + + match addr.ss_family as c_int { + libc::AF_INET => { + assert!(len as usize == mem::size_of::<sockaddr_in>()); + let ret = *(addr as *const _ as *const sockaddr_in); + Ok(SockAddr::Inet(InetAddr::V4(ret))) + } + libc::AF_INET6 => { + assert!(len as usize == mem::size_of::<sockaddr_in6>()); + Ok(SockAddr::Inet(InetAddr::V6(*(addr as *const _ as *const sockaddr_in6)))) + } + libc::AF_UNIX => { + let sun = *(addr as *const _ as *const sockaddr_un); + let pathlen = len - offset_of!(sockaddr_un, sun_path); + Ok(SockAddr::Unix(UnixAddr(sun, pathlen))) + } + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_NETLINK => { + use libc::sockaddr_nl; + Ok(SockAddr::Netlink(NetlinkAddr(*(addr as *const _ as *const sockaddr_nl)))) + } + af => panic!("unexpected address family {}", af), + } +} + + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum Shutdown { + /// Further receptions will be disallowed. + Read, + /// Further transmissions will be disallowed. + Write, + /// Further receptions and transmissions will be disallowed. + Both, +} + +/// Shut down part of a full-duplex connection. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html) +pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> { + unsafe { + use libc::shutdown; + + let how = match how { + Shutdown::Read => libc::SHUT_RD, + Shutdown::Write => libc::SHUT_WR, + Shutdown::Both => libc::SHUT_RDWR, + }; + + Errno::result(shutdown(df, how)).map(drop) + } +} diff --git a/third_party/rust/nix/src/sys/socket/sockopt.rs b/third_party/rust/nix/src/sys/socket/sockopt.rs new file mode 100644 index 0000000000..6c5f450dff --- /dev/null +++ b/third_party/rust/nix/src/sys/socket/sockopt.rs @@ -0,0 +1,631 @@ +use super::{GetSockOpt, SetSockOpt}; +use Result; +use errno::Errno; +use sys::time::TimeVal; +use libc::{self, c_int, c_void, socklen_t}; +use std::mem; +use std::os::unix::io::RawFd; +use std::ffi::{OsStr, OsString}; +#[cfg(target_family = "unix")] +use std::os::unix::ffi::OsStrExt; + +// Constants +// TCP_CA_NAME_MAX isn't defined in user space include files +#[cfg(any(target_os = "freebsd", target_os = "linux"))] +const TCP_CA_NAME_MAX: usize = 16; + +/// Helper for implementing `SetSockOpt` for a given socket option. See +/// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html). +/// +/// This macro aims to help implementing `SetSockOpt` for different socket options that accept +/// different kinds of data to be used with `setsockopt`. +/// +/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option +/// you are implementing represents a simple type. +/// +/// # Arguments +/// +/// * `$name:ident`: name of the type you want to implement `SetSockOpt` for. +/// * `$level:path` : socket layer, or a `protocol level`: could be *raw sockets* +/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), +/// and more. Please refer to your system manual for more options. Will be passed as the second +/// argument (`level`) to the `setsockopt` call. +/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`, +/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`) +/// to the `setsockopt` call. +/// * Type of the value that you are going to set. +/// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for +/// `bool`, `SetUsize` for `usize`, etc.). +macro_rules! setsockopt_impl { + ($name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => { + impl SetSockOpt for $name { + type Val = $ty; + + fn set(&self, fd: RawFd, val: &$ty) -> Result<()> { + unsafe { + let setter: $setter = Set::new(val); + + let res = libc::setsockopt(fd, $level, $flag, + setter.ffi_ptr(), + setter.ffi_len()); + Errno::result(res).map(drop) + } + } + } + } +} + +/// Helper for implementing `GetSockOpt` for a given socket option. See +/// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html). +/// +/// This macro aims to help implementing `GetSockOpt` for different socket options that accept +/// different kinds of data to be use with `getsockopt`. +/// +/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option +/// you are implementing represents a simple type. +/// +/// # Arguments +/// +/// * Name of the type you want to implement `GetSockOpt` for. +/// * Socket layer, or a `protocol level`: could be *raw sockets* (`lic::SOL_SOCKET`), *ip +/// protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), and more. Please refer +/// to your system manual for more options. Will be passed as the second argument (`level`) to +/// the `getsockopt` call. +/// * A flag to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`, +/// `libc::SO_ORIGINAL_DST` and others. Will be passed as the third argument (`option_name`) to +/// the `getsockopt` call. +/// * Type of the value that you are going to get. +/// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for +/// `bool`, `GetUsize` for `usize`, etc.). +macro_rules! getsockopt_impl { + ($name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => { + impl GetSockOpt for $name { + type Val = $ty; + + fn get(&self, fd: RawFd) -> Result<$ty> { + unsafe { + let mut getter: $getter = Get::blank(); + + let res = libc::getsockopt(fd, $level, $flag, + getter.ffi_ptr(), + getter.ffi_len()); + Errno::result(res)?; + + Ok(getter.unwrap()) + } + } + } + } +} + +/// Helper to generate the sockopt accessors. See +/// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html) and +/// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html). +/// +/// This macro aims to help implementing `GetSockOpt` and `SetSockOpt` for different socket options +/// that accept different kinds of data to be use with `getsockopt` and `setsockopt` respectively. +/// +/// Basically this macro wraps up the [`getsockopt_impl!`](macro.getsockopt_impl.html) and +/// [`setsockopt_impl!`](macro.setsockopt_impl.html) macros. +/// +/// # Arguments +/// +/// * `GetOnly`, `SetOnly` or `Both`: whether you want to implement only getter, only setter or +/// both of them. +/// * `$name:ident`: name of type `GetSockOpt`/`SetSockOpt` will be implemented for. +/// * `$level:path` : socket layer, or a `protocol level`: could be *raw sockets* +/// (`lic::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), +/// and more. Please refer to your system manual for more options. Will be passed as the second +/// argument (`level`) to the `getsockopt`/`setsockopt` call. +/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`, +/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`) +/// to the `setsockopt`/`getsockopt` call. +/// * `$ty:ty`: type of the value that will be get/set. +/// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`. +/// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`. +macro_rules! sockopt_impl { + (GetOnly, $name:ident, $level:path, $flag:path, bool) => { + sockopt_impl!(GetOnly, $name, $level, $flag, bool, GetBool); + }; + + (GetOnly, $name:ident, $level:path, $flag:path, u8) => { + sockopt_impl!(GetOnly, $name, $level, $flag, u8, GetU8); + }; + + (GetOnly, $name:ident, $level:path, $flag:path, usize) => { + sockopt_impl!(GetOnly, $name, $level, $flag, usize, GetUsize); + }; + + (SetOnly, $name:ident, $level:path, $flag:path, bool) => { + sockopt_impl!(SetOnly, $name, $level, $flag, bool, SetBool); + }; + + (SetOnly, $name:ident, $level:path, $flag:path, u8) => { + sockopt_impl!(SetOnly, $name, $level, $flag, u8, SetU8); + }; + + (SetOnly, $name:ident, $level:path, $flag:path, usize) => { + sockopt_impl!(SetOnly, $name, $level, $flag, usize, SetUsize); + }; + + (Both, $name:ident, $level:path, $flag:path, bool) => { + sockopt_impl!(Both, $name, $level, $flag, bool, GetBool, SetBool); + }; + + (Both, $name:ident, $level:path, $flag:path, u8) => { + sockopt_impl!(Both, $name, $level, $flag, u8, GetU8, SetU8); + }; + + (Both, $name:ident, $level:path, $flag:path, usize) => { + sockopt_impl!(Both, $name, $level, $flag, usize, GetUsize, SetUsize); + }; + + (Both, $name:ident, $level:path, $flag:path, OsString<$array:ty>) => { + sockopt_impl!(Both, $name, $level, $flag, OsString, GetOsString<$array>, SetOsString); + }; + + /* + * Matchers with generic getter types must be placed at the end, so + * they'll only match _after_ specialized matchers fail + */ + (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => { + sockopt_impl!(GetOnly, $name, $level, $flag, $ty, GetStruct<$ty>); + }; + + (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => { + #[derive(Copy, Clone, Debug)] + pub struct $name; + + getsockopt_impl!($name, $level, $flag, $ty, $getter); + }; + + (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => { + sockopt_impl!(SetOnly, $name, $level, $flag, $ty, SetStruct<$ty>); + }; + + (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => { + #[derive(Copy, Clone, Debug)] + pub struct $name; + + setsockopt_impl!($name, $level, $flag, $ty, $setter); + }; + + (Both, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty, $setter:ty) => { + #[derive(Copy, Clone, Debug)] + pub struct $name; + + setsockopt_impl!($name, $level, $flag, $ty, $setter); + getsockopt_impl!($name, $level, $flag, $ty, $getter); + }; + + (Both, $name:ident, $level:path, $flag:path, $ty:ty) => { + sockopt_impl!(Both, $name, $level, $flag, $ty, GetStruct<$ty>, SetStruct<$ty>); + }; +} + +/* + * + * ===== Define sockopts ===== + * + */ + +sockopt_impl!(Both, ReuseAddr, libc::SOL_SOCKET, libc::SO_REUSEADDR, bool); +sockopt_impl!(Both, ReusePort, libc::SOL_SOCKET, libc::SO_REUSEPORT, bool); +sockopt_impl!(Both, TcpNoDelay, libc::IPPROTO_TCP, libc::TCP_NODELAY, bool); +sockopt_impl!(Both, Linger, libc::SOL_SOCKET, libc::SO_LINGER, libc::linger); +sockopt_impl!(SetOnly, IpAddMembership, libc::IPPROTO_IP, libc::IP_ADD_MEMBERSHIP, super::IpMembershipRequest); +sockopt_impl!(SetOnly, IpDropMembership, libc::IPPROTO_IP, libc::IP_DROP_MEMBERSHIP, super::IpMembershipRequest); +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + sockopt_impl!(SetOnly, Ipv6AddMembership, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest); + sockopt_impl!(SetOnly, Ipv6DropMembership, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest); + } else if #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] { + sockopt_impl!(SetOnly, Ipv6AddMembership, libc::IPPROTO_IPV6, libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest); + sockopt_impl!(SetOnly, Ipv6DropMembership, libc::IPPROTO_IPV6, libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest); + } +} +sockopt_impl!(Both, IpMulticastTtl, libc::IPPROTO_IP, libc::IP_MULTICAST_TTL, u8); +sockopt_impl!(Both, IpMulticastLoop, libc::IPPROTO_IP, libc::IP_MULTICAST_LOOP, bool); +sockopt_impl!(Both, ReceiveTimeout, libc::SOL_SOCKET, libc::SO_RCVTIMEO, TimeVal); +sockopt_impl!(Both, SendTimeout, libc::SOL_SOCKET, libc::SO_SNDTIMEO, TimeVal); +sockopt_impl!(Both, Broadcast, libc::SOL_SOCKET, libc::SO_BROADCAST, bool); +sockopt_impl!(Both, OobInline, libc::SOL_SOCKET, libc::SO_OOBINLINE, bool); +sockopt_impl!(GetOnly, SocketError, libc::SOL_SOCKET, libc::SO_ERROR, i32); +sockopt_impl!(Both, KeepAlive, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(GetOnly, PeerCredentials, libc::SOL_SOCKET, libc::SO_PEERCRED, super::UnixCredentials); +#[cfg(any(target_os = "ios", + target_os = "macos"))] +sockopt_impl!(Both, TcpKeepAlive, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, u32); +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "nacl"))] +sockopt_impl!(Both, TcpKeepIdle, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, u32); +sockopt_impl!(Both, RcvBuf, libc::SOL_SOCKET, libc::SO_RCVBUF, usize); +sockopt_impl!(Both, SndBuf, libc::SOL_SOCKET, libc::SO_SNDBUF, usize); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(SetOnly, RcvBufForce, libc::SOL_SOCKET, libc::SO_RCVBUFFORCE, usize); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(SetOnly, SndBufForce, libc::SOL_SOCKET, libc::SO_SNDBUFFORCE, usize); +sockopt_impl!(GetOnly, SockType, libc::SOL_SOCKET, libc::SO_TYPE, super::SockType); +sockopt_impl!(GetOnly, AcceptConn, libc::SOL_SOCKET, libc::SO_ACCEPTCONN, bool); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(GetOnly, OriginalDst, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::sockaddr_in); +sockopt_impl!(Both, ReceiveTimestamp, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(Both, IpTransparent, libc::SOL_IP, libc::IP_TRANSPARENT, bool); +#[cfg(target_os = "openbsd")] +sockopt_impl!(Both, BindAny, libc::SOL_SOCKET, libc::SO_BINDANY, bool); +#[cfg(target_os = "freebsd")] +sockopt_impl!(Both, BindAny, libc::IPPROTO_IP, libc::IP_BINDANY, bool); +#[cfg(target_os = "linux")] +sockopt_impl!(Both, Mark, libc::SOL_SOCKET, libc::SO_MARK, u32); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(Both, PassCred, libc::SOL_SOCKET, libc::SO_PASSCRED, bool); +#[cfg(any(target_os = "freebsd", target_os = "linux"))] +sockopt_impl!(Both, TcpCongestion, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsString<[u8; TCP_CA_NAME_MAX]>); +#[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" +))] +sockopt_impl!(Both, Ipv4PacketInfo, libc::IPPROTO_IP, libc::IP_PKTINFO, bool); +#[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" +))] +sockopt_impl!(Both, Ipv6RecvPacketInfo, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, bool); + + +/* + * + * ===== Accessor helpers ===== + * + */ + +/// Helper trait that describes what is expected from a `GetSockOpt` getter. +unsafe trait Get<T> { + /// Returns an empty value. + unsafe fn blank() -> Self; + /// Returns a pointer to the stored value. This pointer will be passed to the system's + /// `getsockopt` call (`man 3p getsockopt`, argument `option_value`). + fn ffi_ptr(&mut self) -> *mut c_void; + /// Returns length of the stored value. This pointer will be passed to the system's + /// `getsockopt` call (`man 3p getsockopt`, argument `option_len`). + fn ffi_len(&mut self) -> *mut socklen_t; + /// Returns the stored value. + unsafe fn unwrap(self) -> T; +} + +/// Helper trait that describes what is expected from a `SetSockOpt` setter. +unsafe trait Set<'a, T> { + /// Initialize the setter with a given value. + fn new(val: &'a T) -> Self; + /// Returns a pointer to the stored value. This pointer will be passed to the system's + /// `setsockopt` call (`man 3p setsockopt`, argument `option_value`). + fn ffi_ptr(&self) -> *const c_void; + /// Returns length of the stored value. This pointer will be passed to the system's + /// `setsockopt` call (`man 3p setsockopt`, argument `option_len`). + fn ffi_len(&self) -> socklen_t; +} + +/// Getter for an arbitrary `struct`. +struct GetStruct<T> { + len: socklen_t, + val: T, +} + +unsafe impl<T> Get<T> for GetStruct<T> { + unsafe fn blank() -> Self { + GetStruct { + len: mem::size_of::<T>() as socklen_t, + val: mem::zeroed(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + &mut self.val as *mut T as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn unwrap(self) -> T { + assert!(self.len as usize == mem::size_of::<T>(), "invalid getsockopt implementation"); + self.val + } +} + +/// Setter for an arbitrary `struct`. +struct SetStruct<'a, T: 'static> { + ptr: &'a T, +} + +unsafe impl<'a, T> Set<'a, T> for SetStruct<'a, T> { + fn new(ptr: &'a T) -> SetStruct<'a, T> { + SetStruct { ptr: ptr } + } + + fn ffi_ptr(&self) -> *const c_void { + self.ptr as *const T as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + mem::size_of::<T>() as socklen_t + } +} + +/// Getter for a boolean value. +struct GetBool { + len: socklen_t, + val: c_int, +} + +unsafe impl Get<bool> for GetBool { + unsafe fn blank() -> Self { + GetBool { + len: mem::size_of::<c_int>() as socklen_t, + val: mem::zeroed(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + &mut self.val as *mut c_int as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn unwrap(self) -> bool { + assert!(self.len as usize == mem::size_of::<c_int>(), "invalid getsockopt implementation"); + self.val != 0 + } +} + +/// Setter for a boolean value. +struct SetBool { + val: c_int, +} + +unsafe impl<'a> Set<'a, bool> for SetBool { + fn new(val: &'a bool) -> SetBool { + SetBool { val: if *val { 1 } else { 0 } } + } + + fn ffi_ptr(&self) -> *const c_void { + &self.val as *const c_int as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + mem::size_of::<c_int>() as socklen_t + } +} + +/// Getter for an `u8` value. +struct GetU8 { + len: socklen_t, + val: u8, +} + +unsafe impl Get<u8> for GetU8 { + unsafe fn blank() -> Self { + GetU8 { + len: mem::size_of::<u8>() as socklen_t, + val: mem::zeroed(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + &mut self.val as *mut u8 as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn unwrap(self) -> u8 { + assert!(self.len as usize == mem::size_of::<u8>(), "invalid getsockopt implementation"); + self.val as u8 + } +} + +/// Setter for an `u8` value. +struct SetU8 { + val: u8, +} + +unsafe impl<'a> Set<'a, u8> for SetU8 { + fn new(val: &'a u8) -> SetU8 { + SetU8 { val: *val as u8 } + } + + fn ffi_ptr(&self) -> *const c_void { + &self.val as *const u8 as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + mem::size_of::<c_int>() as socklen_t + } +} + +/// Getter for an `usize` value. +struct GetUsize { + len: socklen_t, + val: c_int, +} + +unsafe impl Get<usize> for GetUsize { + unsafe fn blank() -> Self { + GetUsize { + len: mem::size_of::<c_int>() as socklen_t, + val: mem::zeroed(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + &mut self.val as *mut c_int as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn unwrap(self) -> usize { + assert!(self.len as usize == mem::size_of::<c_int>(), "invalid getsockopt implementation"); + self.val as usize + } +} + +/// Setter for an `usize` value. +struct SetUsize { + val: c_int, +} + +unsafe impl<'a> Set<'a, usize> for SetUsize { + fn new(val: &'a usize) -> SetUsize { + SetUsize { val: *val as c_int } + } + + fn ffi_ptr(&self) -> *const c_void { + &self.val as *const c_int as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + mem::size_of::<c_int>() as socklen_t + } +} + +/// Getter for a `OsString` value. +struct GetOsString<T: AsMut<[u8]>> { + len: socklen_t, + val: T, +} + +unsafe impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> { + unsafe fn blank() -> Self { + GetOsString { + len: mem::size_of::<T>() as socklen_t, + val: mem::zeroed(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + &mut self.val as *mut T as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn unwrap(mut self) -> OsString { + OsStr::from_bytes(self.val.as_mut()).to_owned() + } +} + +/// Setter for a `OsString` value. +struct SetOsString<'a> { + val: &'a OsStr, +} + +unsafe impl<'a> Set<'a, OsString> for SetOsString<'a> { + fn new(val: &'a OsString) -> SetOsString { + SetOsString { val: val.as_os_str() } + } + + fn ffi_ptr(&self) -> *const c_void { + self.val.as_bytes().as_ptr() as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + self.val.len() as socklen_t + } +} + + +#[cfg(test)] +mod test { + #[cfg(any(target_os = "android", target_os = "linux"))] + #[test] + fn can_get_peercred_on_unix_socket() { + use super::super::*; + + let (a, b) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()).unwrap(); + let a_cred = getsockopt(a, super::PeerCredentials).unwrap(); + let b_cred = getsockopt(b, super::PeerCredentials).unwrap(); + assert_eq!(a_cred, b_cred); + assert!(a_cred.pid() != 0); + } + + #[test] + fn is_socket_type_unix() { + use super::super::*; + use ::unistd::close; + + let (a, b) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()).unwrap(); + let a_type = getsockopt(a, super::SockType).unwrap(); + assert!(a_type == SockType::Stream); + close(a).unwrap(); + close(b).unwrap(); + } + + #[test] + fn is_socket_type_dgram() { + use super::super::*; + use ::unistd::close; + + let s = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None).unwrap(); + let s_type = getsockopt(s, super::SockType).unwrap(); + assert!(s_type == SockType::Datagram); + close(s).unwrap(); + } + + #[cfg(any(target_os = "freebsd", + target_os = "linux", + target_os = "nacl"))] + #[test] + fn can_get_listen_on_tcp_socket() { + use super::super::*; + use ::unistd::close; + + let s = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap(); + let s_listening = getsockopt(s, super::AcceptConn).unwrap(); + assert!(!s_listening); + listen(s, 10).unwrap(); + let s_listening2 = getsockopt(s, super::AcceptConn).unwrap(); + assert!(s_listening2); + close(s).unwrap(); + } + + #[cfg(target_os = "linux")] + #[test] + fn is_so_mark_functional() { + use super::super::*; + use ::unistd::Uid; + use ::std::io::{self, Write}; + + if !Uid::current().is_root() { + let stderr = io::stderr(); + let mut handle = stderr.lock(); + writeln!(handle, "SO_MARK requires root privileges. Skipping test.").unwrap(); + return; + } + + let s = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap(); + setsockopt(s, super::Mark, &1337).unwrap(); + let mark = getsockopt(s, super::Mark).unwrap(); + assert_eq!(mark, 1337); + } +} |