use super::sa_family_t; use crate::{Error, Result, NixPath}; use crate::errno::Errno; use memoffset::offset_of; use std::{fmt, mem, net, ptr, slice}; use std::ffi::OsStr; use std::hash::{Hash, Hasher}; use std::path::Path; use std::os::unix::ffi::OsStrExt; #[cfg(any(target_os = "android", target_os = "linux"))] use crate::sys::socket::addr::netlink::NetlinkAddr; #[cfg(any(target_os = "android", target_os = "linux"))] use crate::sys::socket::addr::alg::AlgAddr; #[cfg(any(target_os = "ios", target_os = "macos"))] use std::os::unix::io::RawFd; #[cfg(any(target_os = "ios", target_os = "macos"))] use crate::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 = "illumos", target_os = "netbsd", target_os = "openbsd", target_os = "fuchsia"))] pub use self::datalink::LinkAddr; #[cfg(any(target_os = "android", target_os = "linux"))] pub use self::vsock::VsockAddr; /// 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", target_os = "illumos", target_os = "fuchsia", target_os = "solaris"))] 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 = "illumos", target_os = "ios", target_os = "macos", target_os = "solaris")))] 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, #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] 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(any(target_os = "android", 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 = "illumos", 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 { 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 = "illumos", target_os = "openbsd"))] libc::AF_LINK => Some(AddressFamily::Link), #[cfg(any(target_os = "android", target_os = "linux"))] libc::AF_VSOCK => Some(AddressFamily::Vsock), _ => None } } } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 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 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()), } } } /* * * ===== IpAddr ===== * */ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 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 #[allow(clippy::many_single_char_names)] #[allow(clippy::too_many_arguments)] 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) } } } /* * * ===== Ipv4Addr ===== * */ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Ipv4Addr(pub libc::in_addr); impl Ipv4Addr { #[allow(clippy::identity_op)] // More readable this way pub fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { let ip = ((u32::from(a) << 24) | (u32::from(b) << 16) | (u32::from(c) << 8) | (u32::from(d) << 0)).to_be(); Ipv4Addr(libc::in_addr { s_addr: ip }) } // Use pass by reference for symmetry with Ipv6Addr::from_std #[allow(clippy::trivially_copy_pass_by_ref)] 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 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]) } } /* * * ===== Ipv6Addr ===== * */ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 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 { #[allow(clippy::many_single_char_names)] #[allow(clippy::too_many_arguments)] pub fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { Ipv6Addr(libc::in6_addr{s6_addr: to_u8_array!(a,b,c,d,e,f,g,h)}) } 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) } } /// 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(Clone, Copy, Debug)] pub struct UnixAddr(pub libc::sockaddr_un, pub usize); impl UnixAddr { /// Create a new sockaddr_un representing a filesystem path. pub fn new(path: &P) -> Result { 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 { 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(::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 fmt::Display for UnixAddr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.1 == 0 { f.write_str("") } 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 PartialEq for UnixAddr { fn eq(&self, other: &UnixAddr) -> bool { self.sun_path() == other.sun_path() } } impl Eq for UnixAddr {} impl Hash for UnixAddr { fn hash(&self, s: &mut H) { ( self.0.sun_family, self.sun_path() ).hash(s) } } /// Represents a socket address #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum SockAddr { Inet(InetAddr), Unix(UnixAddr), #[cfg(any(target_os = "android", target_os = "linux"))] Netlink(NetlinkAddr), #[cfg(any(target_os = "android", target_os = "linux"))] Alg(AlgAddr), #[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 = "illumos", target_os = "netbsd", target_os = "openbsd"))] Link(LinkAddr), #[cfg(any(target_os = "android", target_os = "linux"))] Vsock(VsockAddr), } impl SockAddr { pub fn new_inet(addr: InetAddr) -> SockAddr { SockAddr::Inet(addr) } pub fn new_unix(path: &P) -> Result { 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 = "android", target_os = "linux"))] pub fn new_alg(alg_type: &str, alg_name: &str) -> SockAddr { SockAddr::Alg(AlgAddr::new(alg_type, alg_name)) } #[cfg(any(target_os = "ios", target_os = "macos"))] pub fn new_sys_control(sockfd: RawFd, name: &str, unit: u32) -> Result { SysControlAddr::from_name(sockfd, name, unit).map(|a| SockAddr::SysControl(a)) } #[cfg(any(target_os = "android", target_os = "linux"))] pub fn new_vsock(cid: u32, port: u32) -> SockAddr { SockAddr::Vsock(VsockAddr::new(cid, port)) } 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 = "android", target_os = "linux"))] SockAddr::Alg(..) => AddressFamily::Alg, #[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 = "illumos", target_os = "openbsd"))] SockAddr::Link(..) => AddressFamily::Link, #[cfg(any(target_os = "android", target_os = "linux"))] SockAddr::Vsock(..) => AddressFamily::Vsock, } } 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. /// /// # Safety /// /// unsafe because it takes a raw pointer as argument. The caller must /// ensure that the pointer is valid. #[cfg(not(target_os = "fuchsia"))] pub(crate) unsafe fn from_libc_sockaddr(addr: *const libc::sockaddr) -> Option { if addr.is_null() { None } else { match AddressFamily::from_i32(i32::from((*addr).sa_family)) { 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 = "illumos", 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)) } }, #[cfg(any(target_os = "android", target_os = "linux"))] Some(AddressFamily::Vsock) => Some(SockAddr::Vsock( VsockAddr(*(addr as *const libc::sockaddr_vm)))), // 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 fn as_ffi_pair(&self) -> (&libc::sockaddr, libc::socklen_t) { match *self { SockAddr::Inet(InetAddr::V4(ref addr)) => ( // This cast is always allowed in C unsafe { &*(addr as *const libc::sockaddr_in as *const libc::sockaddr) }, mem::size_of_val(addr) as libc::socklen_t ), SockAddr::Inet(InetAddr::V6(ref addr)) => ( // This cast is always allowed in C unsafe { &*(addr as *const libc::sockaddr_in6 as *const libc::sockaddr) }, mem::size_of_val(addr) as libc::socklen_t ), SockAddr::Unix(UnixAddr(ref addr, len)) => ( // This cast is always allowed in C unsafe { &*(addr as *const libc::sockaddr_un as *const libc::sockaddr) }, (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)) => ( // This cast is always allowed in C unsafe { &*(sa as *const libc::sockaddr_nl as *const libc::sockaddr) }, mem::size_of_val(sa) as libc::socklen_t ), #[cfg(any(target_os = "android", target_os = "linux"))] SockAddr::Alg(AlgAddr(ref sa)) => ( // This cast is always allowed in C unsafe { &*(sa as *const libc::sockaddr_alg as *const libc::sockaddr) }, mem::size_of_val(sa) as libc::socklen_t ), #[cfg(any(target_os = "ios", target_os = "macos"))] SockAddr::SysControl(SysControlAddr(ref sa)) => ( // This cast is always allowed in C unsafe { &*(sa as *const libc::sockaddr_ctl as *const libc::sockaddr) }, mem::size_of_val(sa) as libc::socklen_t ), #[cfg(any(target_os = "android", target_os = "linux"))] SockAddr::Link(LinkAddr(ref addr)) => ( // This cast is always allowed in C unsafe { &*(addr as *const libc::sockaddr_ll as *const libc::sockaddr) }, mem::size_of_val(addr) as libc::socklen_t ), #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "illumos", target_os = "netbsd", target_os = "openbsd"))] SockAddr::Link(LinkAddr(ref addr)) => ( // This cast is always allowed in C unsafe { &*(addr as *const libc::sockaddr_dl as *const libc::sockaddr) }, mem::size_of_val(addr) as libc::socklen_t ), #[cfg(any(target_os = "android", target_os = "linux"))] SockAddr::Vsock(VsockAddr(ref sa)) => ( // This cast is always allowed in C unsafe { &*(sa as *const libc::sockaddr_vm as *const libc::sockaddr) }, mem::size_of_val(sa) as libc::socklen_t ), } } } 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 = "android", target_os = "linux"))] SockAddr::Alg(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 = "illumos", target_os = "openbsd"))] SockAddr::Link(ref ether_addr) => ether_addr.fmt(f), #[cfg(any(target_os = "android", target_os = "linux"))] SockAddr::Vsock(ref svm) => svm.fmt(f), } } } #[cfg(any(target_os = "android", target_os = "linux"))] pub mod netlink { use crate::sys::socket::addr::AddressFamily; use libc::{sa_family_t, sockaddr_nl}; use std::{fmt, mem}; #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct NetlinkAddr(pub sockaddr_nl); 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()) } } } #[cfg(any(target_os = "android", target_os = "linux"))] pub mod alg { use libc::{AF_ALG, sockaddr_alg, c_char}; use std::{fmt, mem, str}; use std::hash::{Hash, Hasher}; use std::ffi::CStr; #[derive(Copy, Clone)] pub struct AlgAddr(pub sockaddr_alg); // , PartialEq, Eq, Debug, Hash impl PartialEq for AlgAddr { fn eq(&self, other: &Self) -> bool { let (inner, other) = (self.0, other.0); (inner.salg_family, &inner.salg_type[..], inner.salg_feat, inner.salg_mask, &inner.salg_name[..]) == (other.salg_family, &other.salg_type[..], other.salg_feat, other.salg_mask, &other.salg_name[..]) } } impl Eq for AlgAddr {} impl Hash for AlgAddr { fn hash(&self, s: &mut H) { let inner = self.0; (inner.salg_family, &inner.salg_type[..], inner.salg_feat, inner.salg_mask, &inner.salg_name[..]).hash(s); } } impl AlgAddr { pub fn new(alg_type: &str, alg_name: &str) -> AlgAddr { let mut addr: sockaddr_alg = unsafe { mem::zeroed() }; addr.salg_family = AF_ALG as u16; addr.salg_type[..alg_type.len()].copy_from_slice(alg_type.to_string().as_bytes()); addr.salg_name[..alg_name.len()].copy_from_slice(alg_name.to_string().as_bytes()); AlgAddr(addr) } pub fn alg_type(&self) -> &CStr { unsafe { CStr::from_ptr(self.0.salg_type.as_ptr() as *const c_char) } } pub fn alg_name(&self) -> &CStr { unsafe { CStr::from_ptr(self.0.salg_name.as_ptr() as *const c_char) } } } impl fmt::Display for AlgAddr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "type: {} alg: {}", self.alg_name().to_string_lossy(), self.alg_type().to_string_lossy()) } } impl fmt::Debug for AlgAddr { 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 crate::sys::socket::addr::AddressFamily; use libc::{self, c_uchar}; use std::{fmt, mem}; use std::os::unix::io::RawFd; use crate::{Errno, Error, Result}; // FIXME: Move type into `libc` #[repr(C)] #[derive(Clone, Copy)] #[allow(missing_debug_implementations)] 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); #[repr(C)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct SysControlAddr(pub libc::sockaddr_ctl); impl SysControlAddr { pub fn new(id: u32, unit: u32) -> SysControlAddr { let addr = libc::sockaddr_ctl { sc_len: mem::size_of::() 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 { 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 }; 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) } } } #[cfg(any(target_os = "android", target_os = "linux", target_os = "fuchsia"))] mod datalink { use super::{fmt, AddressFamily}; /// Hardware Address #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 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] { [ self.0.sll_addr[0] as u8, self.0.sll_addr[1] as u8, self.0.sll_addr[2] as u8, self.0.sll_addr[3] as u8, self.0.sll_addr[4] as u8, self.0.sll_addr[5] as u8, ] } } 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]) } } } #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "illumos", target_os = "netbsd", target_os = "openbsd"))] mod datalink { use super::{fmt, AddressFamily}; /// Hardware Address #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct LinkAddr(pub libc::sockaddr_dl); impl LinkAddr { /// Total length of sockaddr #[cfg(not(target_os = "illumos"))] pub fn len(&self) -> usize { self.0.sdl_len as usize } /// always == AF_LINK pub fn family(&self) -> AddressFamily { assert_eq!(i32::from(self.0.sdl_family), 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(); alen == 0 || nlen + alen >= data_len } /// Physical-layer address (MAC) pub fn addr(&self) -> [u8; 6] { let nlen = self.nlen(); let data = self.0.sdl_data; assert!(!self.is_empty()); [ data[nlen] as u8, data[nlen + 1] as u8, data[nlen + 2] as u8, data[nlen + 3] as u8, data[nlen + 4] as u8, data[nlen + 5] as u8, ] } } 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]) } } } #[cfg(any(target_os = "android", target_os = "linux"))] pub mod vsock { use crate::sys::socket::addr::AddressFamily; use libc::{sa_family_t, sockaddr_vm}; use std::{fmt, mem}; use std::hash::{Hash, Hasher}; #[derive(Copy, Clone)] pub struct VsockAddr(pub sockaddr_vm); impl PartialEq for VsockAddr { fn eq(&self, other: &Self) -> bool { let (inner, other) = (self.0, other.0); (inner.svm_family, inner.svm_cid, inner.svm_port) == (other.svm_family, other.svm_cid, other.svm_port) } } impl Eq for VsockAddr {} impl Hash for VsockAddr { fn hash(&self, s: &mut H) { let inner = self.0; (inner.svm_family, inner.svm_cid, inner.svm_port).hash(s); } } /// VSOCK Address /// /// The address for AF_VSOCK socket is defined as a combination of a /// 32-bit Context Identifier (CID) and a 32-bit port number. impl VsockAddr { pub fn new(cid: u32, port: u32) -> VsockAddr { let mut addr: sockaddr_vm = unsafe { mem::zeroed() }; addr.svm_family = AddressFamily::Vsock as sa_family_t; addr.svm_cid = cid; addr.svm_port = port; VsockAddr(addr) } /// Context Identifier (CID) pub fn cid(&self) -> u32 { self.0.svm_cid } /// Port number pub fn port(&self) -> u32 { self.0.svm_port } } impl fmt::Display for VsockAddr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "cid: {} port: {}", self.cid(), self.port()) } } impl fmt::Debug for VsockAddr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) } } } #[cfg(test)] mod tests { #[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 = "illumos", 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!() } }; } #[cfg(target_os = "illumos")] #[test] fn test_illumos_tap_datalink_addr() { let bytes = [25u8, 0, 0, 0, 6, 0, 6, 0, 24, 101, 144, 221, 76, 176]; 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!() } }; } #[cfg(any(target_os = "android", target_os = "linux"))] #[test] fn test_abstract_sun_path() { let name = String::from("nix\0abstract\0test"); let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap(); let sun_path1 = addr.sun_path(); let sun_path2 = [0u8, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, 115, 116]; assert_eq!(sun_path1.len(), sun_path2.len()); for i in 0..sun_path1.len() { assert_eq!(sun_path1[i], sun_path2[i]); } } }