#[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 = "haiku",
    target_os = "fuchsia",
    target_os = "aix",
))]
#[cfg(feature = "net")]
pub use self::datalink::LinkAddr;
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
pub use self::vsock::VsockAddr;
use super::sa_family_t;
use crate::errno::Errno;
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::sys::socket::addr::alg::AlgAddr;
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::sys::socket::addr::netlink::NetlinkAddr;
#[cfg(all(
    feature = "ioctl",
    any(target_os = "ios", target_os = "macos")
))]
use crate::sys::socket::addr::sys_control::SysControlAddr;
use crate::{NixPath, Result};
use cfg_if::cfg_if;
use memoffset::offset_of;
use std::convert::TryInto;
use std::ffi::OsStr;
use std::hash::{Hash, Hasher};
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::{fmt, mem, net, ptr, slice};

/// Convert a std::net::Ipv4Addr into the libc form.
#[cfg(feature = "net")]
pub(crate) const fn ipv4addr_to_libc(addr: net::Ipv4Addr) -> libc::in_addr {
    libc::in_addr {
        s_addr: u32::from_ne_bytes(addr.octets())
    }
}

/// Convert a std::net::Ipv6Addr into the libc form.
#[cfg(feature = "net")]
pub(crate) const fn ipv6addr_to_libc(addr: &net::Ipv6Addr) -> libc::in6_addr {
    libc::in6_addr {
        s6_addr: addr.octets()
    }
}

/// These constants specify the protocol family to be used
/// in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
///
/// # References
///
/// [address_families(7)](https://man7.org/linux/man-pages/man7/address_families.7.html)
// Should this be u8?
#[repr(i32)]
#[non_exhaustive]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum AddressFamily {
    /// Local communication (see [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html))
    Unix = libc::AF_UNIX,
    /// IPv4 Internet protocols (see [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html))
    Inet = libc::AF_INET,
    /// IPv6 Internet protocols (see [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html))
    Inet6 = libc::AF_INET6,
    /// Kernel user interface device (see [`netlink(7)`](https://man7.org/linux/man-pages/man7/netlink.7.html))
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Netlink = libc::AF_NETLINK,
    /// Kernel interface for interacting with the routing table
    #[cfg(not(any(
        target_os = "redox",
        target_os = "linux",
        target_os = "android"
    )))]
    Route = libc::PF_ROUTE,
    /// Low level packet interface (see [`packet(7)`](https://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"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Packet = libc::AF_PACKET,
    /// KEXT Controls and Notifications
    #[cfg(any(target_os = "ios", target_os = "macos"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    System = libc::AF_SYSTEM,
    /// Amateur radio AX.25 protocol
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Ax25 = libc::AF_AX25,
    /// IPX - Novell protocols
    #[cfg(not(any(target_os = "aix", target_os = "redox")))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Ipx = libc::AF_IPX,
    /// AppleTalk
    #[cfg(not(target_os = "redox"))]
    AppleTalk = libc::AF_APPLETALK,
    /// AX.25 packet layer protocol.
    /// (see [netrom(4)](https://www.unix.com/man-page/linux/4/netrom/))
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    NetRom = libc::AF_NETROM,
    /// Can't be used for creating sockets; mostly used for bridge
    /// links in
    /// [rtnetlink(7)](https://man7.org/linux/man-pages/man7/rtnetlink.7.html)
    /// protocol commands.
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Bridge = libc::AF_BRIDGE,
    /// Access to raw ATM PVCs
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    AtmPvc = libc::AF_ATMPVC,
    /// ITU-T X.25 / ISO-8208 protocol (see [`x25(7)`](https://man7.org/linux/man-pages/man7/x25.7.html))
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    X25 = libc::AF_X25,
    /// RATS (Radio Amateur Telecommunications Society) Open
    /// Systems environment (ROSE) AX.25 packet layer protocol.
    /// (see [netrom(4)](https://www.unix.com/man-page/linux/4/netrom/))
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Rose = libc::AF_ROSE,
    /// DECet protocol sockets.
    #[cfg(not(any(target_os = "haiku", target_os = "redox")))]
    Decnet = libc::AF_DECnet,
    /// Reserved for "802.2LLC project"; never used.
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    NetBeui = libc::AF_NETBEUI,
    /// This was a short-lived (between Linux 2.1.30 and
    /// 2.1.99pre2) protocol family for firewall upcalls.
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Security = libc::AF_SECURITY,
    /// Key management protocol.
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Key = libc::AF_KEY,
    #[allow(missing_docs)] // Not documented anywhere that I can find
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Ash = libc::AF_ASH,
    /// Acorn Econet protocol
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Econet = libc::AF_ECONET,
    /// Access to ATM Switched Virtual Circuits
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    AtmSvc = libc::AF_ATMSVC,
    /// Reliable Datagram Sockets (RDS) protocol
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Rds = libc::AF_RDS,
    /// IBM SNA
    #[cfg(not(any(target_os = "haiku", target_os = "redox")))]
    Sna = libc::AF_SNA,
    /// Socket interface over IrDA
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Irda = libc::AF_IRDA,
    /// Generic PPP transport layer, for setting up L2 tunnels (L2TP and PPPoE)
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Pppox = libc::AF_PPPOX,
    /// Legacy protocol for wide area network (WAN) connectivity that was used
    /// by Sangoma WAN cards
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Wanpipe = libc::AF_WANPIPE,
    /// Logical link control (IEEE 802.2 LLC) protocol
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Llc = libc::AF_LLC,
    /// InfiniBand native addressing
    #[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Ib = libc::AF_IB,
    /// Multiprotocol Label Switching
    #[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Mpls = libc::AF_MPLS,
    /// Controller Area Network automotive bus protocol
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Can = libc::AF_CAN,
    /// TIPC, "cluster domain sockets" protocol
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Tipc = libc::AF_TIPC,
    /// Bluetooth low-level socket protocol
    #[cfg(not(any(
        target_os = "aix",
        target_os = "illumos",
        target_os = "ios",
        target_os = "macos",
        target_os = "solaris",
        target_os = "redox",
    )))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Bluetooth = libc::AF_BLUETOOTH,
    /// IUCV (inter-user communication vehicle) z/VM protocol for
    /// hypervisor-guest interaction
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Iucv = libc::AF_IUCV,
    /// Rx, Andrew File System remote procedure call protocol
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    RxRpc = libc::AF_RXRPC,
    /// New "modular ISDN" driver interface protocol
    #[cfg(not(any(
        target_os = "aix",
        target_os = "illumos",
        target_os = "solaris",
        target_os = "haiku",
        target_os = "redox",
    )))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Isdn = libc::AF_ISDN,
    /// Nokia cellular modem IPC/RPC interface
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Phonet = libc::AF_PHONET,
    /// IEEE 802.15.4 WPAN (wireless personal area network) raw packet protocol
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Ieee802154 = libc::AF_IEEE802154,
    /// Ericsson's Communication CPU to Application CPU interface (CAIF)
    /// protocol.
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Caif = libc::AF_CAIF,
    /// Interface to kernel crypto API
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Alg = libc::AF_ALG,
    /// Near field communication
    #[cfg(target_os = "linux")]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Nfc = libc::AF_NFC,
    /// VMWare VSockets protocol for hypervisor-guest interaction.
    #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Vsock = libc::AF_VSOCK,
    /// ARPANet IMP addresses
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    ImpLink = libc::AF_IMPLINK,
    /// PUP protocols, e.g. BSP
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Pup = libc::AF_PUP,
    /// MIT CHAOS protocols
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Chaos = libc::AF_CHAOS,
    /// Novell and Xerox protocol
    #[cfg(any(
        target_os = "ios",
        target_os = "macos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Ns = libc::AF_NS,
    #[allow(missing_docs)] // Not documented anywhere that I can find
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Iso = libc::AF_ISO,
    /// Bell Labs virtual circuit switch ?
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Datakit = libc::AF_DATAKIT,
    /// CCITT protocols, X.25 etc
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Ccitt = libc::AF_CCITT,
    /// DEC Direct data link interface
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Dli = libc::AF_DLI,
    #[allow(missing_docs)] // Not documented anywhere that I can find
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Lat = libc::AF_LAT,
    /// NSC Hyperchannel
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Hylink = libc::AF_HYLINK,
    /// Link layer interface
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "illumos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Link = libc::AF_LINK,
    /// connection-oriented IP, aka ST II
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Coip = libc::AF_COIP,
    /// Computer Network Technology
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Cnt = libc::AF_CNT,
    /// Native ATM access
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    Natm = libc::AF_NATM,
    /// Unspecified address family, (see [`getaddrinfo(3)`](https://man7.org/linux/man-pages/man3/getaddrinfo.3.html))
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    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 const 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(not(any(
                target_os = "redox",
                target_os = "linux",
                target_os = "android"
            )))]
            libc::PF_ROUTE => Some(AddressFamily::Route),
            #[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", target_os = "macos"))]
            libc::AF_VSOCK => Some(AddressFamily::Vsock),
            _ => None,
        }
    }
}

/// A wrapper around `sockaddr_un`.
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct UnixAddr {
    // INVARIANT: sun & sun_len are valid as defined by docs for from_raw_parts
    sun: libc::sockaddr_un,
    /// The length of the valid part of `sun`, including the sun_family field
    /// but excluding any trailing nul.
    // On the BSDs, this field is built into sun
    #[cfg(any(
        target_os = "android",
        target_os = "fuchsia",
        target_os = "illumos",
        target_os = "linux",
        target_os = "redox",
    ))]
    sun_len: u8,
}

// linux man page unix(7) says there are 3 kinds of unix socket:
// pathname: addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1
// unnamed: addrlen = sizeof(sa_family_t)
// abstract: addren > sizeof(sa_family_t), name = sun_path[..(addrlen - sizeof(sa_family_t))]
//
// what we call path_len = addrlen - offsetof(struct sockaddr_un, sun_path)
#[derive(PartialEq, Eq, Hash)]
enum UnixAddrKind<'a> {
    Pathname(&'a Path),
    Unnamed,
    #[cfg(any(target_os = "android", target_os = "linux"))]
    Abstract(&'a [u8]),
}
impl<'a> UnixAddrKind<'a> {
    /// Safety: sun & sun_len must be valid
    #[allow(clippy::unnecessary_cast)]   // Not unnecessary on all platforms
    unsafe fn get(sun: &'a libc::sockaddr_un, sun_len: u8) -> Self {
        assert!(sun_len as usize >= offset_of!(libc::sockaddr_un, sun_path));
        let path_len =
            sun_len as usize - offset_of!(libc::sockaddr_un, sun_path);
        if path_len == 0 {
            return Self::Unnamed;
        }
        #[cfg(any(target_os = "android", target_os = "linux"))]
        if sun.sun_path[0] == 0 {
            let name = slice::from_raw_parts(
                sun.sun_path.as_ptr().add(1) as *const u8,
                path_len - 1,
            );
            return Self::Abstract(name);
        }
        let pathname =
            slice::from_raw_parts(sun.sun_path.as_ptr() as *const u8, path_len);
        if pathname.last() == Some(&0) {
            // A trailing NUL is not considered part of the path, and it does
            // not need to be included in the addrlen passed to functions like
            // bind().  However, Linux adds a trailing NUL, even if one was not
            // originally present, when returning addrs from functions like
            // getsockname() (the BSDs do not do that).  So we need to filter
            // out any trailing NUL here, so sockaddrs can round-trip through
            // the kernel and still compare equal.
            Self::Pathname(Path::new(OsStr::from_bytes(
                &pathname[0..pathname.len() - 1],
            )))
        } else {
            Self::Pathname(Path::new(OsStr::from_bytes(pathname)))
        }
    }
}

impl UnixAddr {
    /// Create a new sockaddr_un representing a filesystem path.
    #[allow(clippy::unnecessary_cast)]   // Not unnecessary on all platforms
    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(Errno::ENAMETOOLONG);
            }

            let sun_len = (bytes.len()
                + offset_of!(libc::sockaddr_un, sun_path))
            .try_into()
            .unwrap();

            #[cfg(any(
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "ios",
                target_os = "macos",
                target_os = "netbsd",
                target_os = "openbsd"
            ))]
            {
                ret.sun_len = sun_len;
            }
            ptr::copy_nonoverlapping(
                bytes.as_ptr(),
                ret.sun_path.as_mut_ptr() as *mut u8,
                bytes.len(),
            );

            Ok(UnixAddr::from_raw_parts(ret, sun_len))
        })?
    }

    /// Create a new `sockaddr_un` representing an address in the "abstract namespace".
    ///
    /// The leading nul byte for the abstract namespace is automatically added;
    /// thus the input `path` is expected to be the bare name, not NUL-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"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    #[allow(clippy::unnecessary_cast)]   // Not unnecessary on all platforms
    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() >= ret.sun_path.len() {
                return Err(Errno::ENAMETOOLONG);
            }
            let sun_len =
                (path.len() + 1 + offset_of!(libc::sockaddr_un, sun_path))
                    .try_into()
                    .unwrap();

            // 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::from_raw_parts(ret, sun_len))
        }
    }

    /// Create a new `sockaddr_un` representing an "unnamed" unix socket address.
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    pub fn new_unnamed() -> UnixAddr {
        let ret = libc::sockaddr_un {
            sun_family: AddressFamily::Unix as sa_family_t,
            ..unsafe { mem::zeroed() }
        };

        let sun_len: u8 =
            offset_of!(libc::sockaddr_un, sun_path).try_into().unwrap();

        unsafe { UnixAddr::from_raw_parts(ret, sun_len) }
    }

    /// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `sun_len`
    /// is the size of the valid portion of the struct, excluding any trailing
    /// NUL.
    ///
    /// # Safety
    /// This pair of sockaddr_un & sun_len must be a valid unix addr, which
    /// means:
    /// - sun_len >= offset_of(sockaddr_un, sun_path)
    /// - sun_len <= sockaddr_un.sun_path.len() - offset_of(sockaddr_un, sun_path)
    /// - if this is a unix addr with a pathname, sun.sun_path is a
    ///   fs path, not necessarily nul-terminated.
    pub(crate) unsafe fn from_raw_parts(
        sun: libc::sockaddr_un,
        sun_len: u8,
    ) -> UnixAddr {
        cfg_if! {
            if #[cfg(any(target_os = "android",
                     target_os = "fuchsia",
                     target_os = "illumos",
                     target_os = "linux",
                     target_os = "redox",
                ))]
            {
                UnixAddr { sun, sun_len }
            } else {
                assert_eq!(sun_len, sun.sun_len);
                UnixAddr {sun}
            }
        }
    }

    fn kind(&self) -> UnixAddrKind<'_> {
        // SAFETY: our sockaddr is always valid because of the invariant on the struct
        unsafe { UnixAddrKind::get(&self.sun, self.sun_len()) }
    }

    /// If this address represents a filesystem path, return that path.
    pub fn path(&self) -> Option<&Path> {
        match self.kind() {
            UnixAddrKind::Pathname(path) => Some(path),
            _ => None,
        }
    }

    /// If this address represents an abstract socket, return its name.
    ///
    /// For abstract sockets only the bare name is returned, without the
    /// leading NUL byte. `None` is returned for unnamed or path-backed sockets.
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    pub fn as_abstract(&self) -> Option<&[u8]> {
        match self.kind() {
            UnixAddrKind::Abstract(name) => Some(name),
            _ => None,
        }
    }

    /// Check if this address is an "unnamed" unix socket address.
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    #[inline]
    pub fn is_unnamed(&self) -> bool {
        matches!(self.kind(), UnixAddrKind::Unnamed)
    }

    /// Returns the addrlen of this socket - `offsetof(struct sockaddr_un, sun_path)`
    #[inline]
    pub fn path_len(&self) -> usize {
        self.sun_len() as usize - offset_of!(libc::sockaddr_un, sun_path)
    }
    /// Returns a pointer to the raw `sockaddr_un` struct
    #[inline]
    pub fn as_ptr(&self) -> *const libc::sockaddr_un {
        &self.sun
    }
    /// Returns a mutable pointer to the raw `sockaddr_un` struct
    #[inline]
    pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_un {
        &mut self.sun
    }

    fn sun_len(&self) -> u8 {
        cfg_if! {
            if #[cfg(any(target_os = "android",
                     target_os = "fuchsia",
                     target_os = "illumos",
                     target_os = "linux",
                     target_os = "redox",
                ))]
            {
                self.sun_len
            } else {
                self.sun.sun_len
            }
        }
    }
}

impl private::SockaddrLikePriv for UnixAddr {}
impl SockaddrLike for UnixAddr {
    #[cfg(any(
        target_os = "android",
        target_os = "fuchsia",
        target_os = "illumos",
        target_os = "linux"
    ))]
    fn len(&self) -> libc::socklen_t {
        self.sun_len.into()
    }

    unsafe fn from_raw(
        addr: *const libc::sockaddr,
        len: Option<libc::socklen_t>,
    ) -> Option<Self>
    where
        Self: Sized,
    {
        if let Some(l) = len {
            if (l as usize) < offset_of!(libc::sockaddr_un, sun_path)
                || l > u8::MAX as libc::socklen_t
            {
                return None;
            }
        }
        if (*addr).sa_family as i32 != libc::AF_UNIX {
            return None;
        }
        let mut su: libc::sockaddr_un = mem::zeroed();
        let sup = &mut su as *mut libc::sockaddr_un as *mut u8;
        cfg_if! {
            if #[cfg(any(target_os = "android",
                         target_os = "fuchsia",
                         target_os = "illumos",
                         target_os = "linux",
                         target_os = "redox",
                ))] {
                let su_len = len.unwrap_or(
                    mem::size_of::<libc::sockaddr_un>() as libc::socklen_t
                );
            } else {
                let su_len = len.unwrap_or((*addr).sa_len as libc::socklen_t);
            }
        };
        ptr::copy(addr as *const u8, sup, su_len as usize);
        Some(Self::from_raw_parts(su, su_len as u8))
    }

    fn size() -> libc::socklen_t
    where
        Self: Sized,
    {
        mem::size_of::<libc::sockaddr_un>() as libc::socklen_t
    }

    unsafe fn set_length(&mut self, new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic> {
        // `new_length` is only used on some platforms, so it must be provided even when not used
        #![allow(unused_variables)]
        cfg_if! {
            if #[cfg(any(target_os = "android",
                         target_os = "fuchsia",
                         target_os = "illumos",
                         target_os = "linux",
                         target_os = "redox",
                ))] {
                self.sun_len = new_length as u8;
            }
        };
        Ok(())
    }
}

impl AsRef<libc::sockaddr_un> for UnixAddr {
    fn as_ref(&self) -> &libc::sockaddr_un {
        &self.sun
    }
}

#[cfg(any(target_os = "android", target_os = "linux"))]
fn fmt_abstract(abs: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
    use fmt::Write;
    f.write_str("@\"")?;
    for &b in abs {
        use fmt::Display;
        char::from(b).escape_default().fmt(f)?;
    }
    f.write_char('"')?;
    Ok(())
}

impl fmt::Display for UnixAddr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.kind() {
            UnixAddrKind::Pathname(path) => path.display().fmt(f),
            UnixAddrKind::Unnamed => f.pad("<unbound UNIX socket>"),
            #[cfg(any(target_os = "android", target_os = "linux"))]
            UnixAddrKind::Abstract(name) => fmt_abstract(name, f),
        }
    }
}

impl PartialEq for UnixAddr {
    fn eq(&self, other: &UnixAddr) -> bool {
        self.kind() == other.kind()
    }
}

impl Eq for UnixAddr {}

impl Hash for UnixAddr {
    fn hash<H: Hasher>(&self, s: &mut H) {
        self.kind().hash(s)
    }
}

/// Anything that, in C, can be cast back and forth to `sockaddr`.
///
/// Most implementors also implement `AsRef<libc::XXX>` to access their
/// inner type read-only.
#[allow(clippy::len_without_is_empty)]
pub trait SockaddrLike: private::SockaddrLikePriv {
    /// Returns a raw pointer to the inner structure.  Useful for FFI.
    fn as_ptr(&self) -> *const libc::sockaddr {
        self as *const Self as *const libc::sockaddr
    }

    /// Unsafe constructor from a variable length source
    ///
    /// Some C APIs from provide `len`, and others do not.  If it's provided it
    /// will be validated.  If not, it will be guessed based on the family.
    ///
    /// # Arguments
    ///
    /// - `addr`:   raw pointer to something that can be cast to a
    ///             `libc::sockaddr`. For example, `libc::sockaddr_in`,
    ///             `libc::sockaddr_in6`, etc.
    /// - `len`:    For fixed-width types like `sockaddr_in`, it will be
    ///             validated if present and ignored if not.  For variable-width
    ///             types it is required and must be the total length of valid
    ///             data.  For example, if `addr` points to a
    ///             named `sockaddr_un`, then `len` must be the length of the
    ///             structure up to but not including the trailing NUL.
    ///
    /// # Safety
    ///
    /// `addr` must be valid for the specific type of sockaddr.  `len`, if
    /// present, must not exceed the length of valid data in `addr`.
    unsafe fn from_raw(
        addr: *const libc::sockaddr,
        len: Option<libc::socklen_t>,
    ) -> Option<Self>
    where
        Self: Sized;

    /// Return the address family of this socket
    ///
    /// # Examples
    /// One common use is to match on the family of a union type, like this:
    /// ```
    /// # use nix::sys::socket::*;
    /// # use std::os::unix::io::AsRawFd;
    /// let fd = socket(AddressFamily::Inet, SockType::Stream,
    ///     SockFlag::empty(), None).unwrap();
    /// let ss: SockaddrStorage = getsockname(fd.as_raw_fd()).unwrap();
    /// match ss.family().unwrap() {
    ///     AddressFamily::Inet => println!("{}", ss.as_sockaddr_in().unwrap()),
    ///     AddressFamily::Inet6 => println!("{}", ss.as_sockaddr_in6().unwrap()),
    ///     _ => println!("Unexpected address family")
    /// }
    /// ```
    fn family(&self) -> Option<AddressFamily> {
        // Safe since all implementors have a sa_family field at the same
        // address, and they're all repr(C)
        AddressFamily::from_i32(unsafe {
            (*(self as *const Self as *const libc::sockaddr)).sa_family as i32
        })
    }

    cfg_if! {
        if #[cfg(any(target_os = "dragonfly",
                  target_os = "freebsd",
                  target_os = "ios",
                  target_os = "macos",
                  target_os = "netbsd",
                  target_os = "openbsd"))] {
            /// Return the length of valid data in the sockaddr structure.
            ///
            /// For fixed-size sockaddrs, this should be the size of the
            /// structure.  But for variable-sized types like [`UnixAddr`] it
            /// may be less.
            fn len(&self) -> libc::socklen_t {
                // Safe since all implementors have a sa_len field at the same
                // address, and they're all repr(transparent).
                // Robust for all implementors.
                unsafe {
                    (*(self as *const Self as *const libc::sockaddr)).sa_len
                }.into()
            }
        } else {
            /// Return the length of valid data in the sockaddr structure.
            ///
            /// For fixed-size sockaddrs, this should be the size of the
            /// structure.  But for variable-sized types like [`UnixAddr`] it
            /// may be less.
            fn len(&self) -> libc::socklen_t {
                // No robust default implementation is possible without an
                // sa_len field.  Implementors with a variable size must
                // override this method.
                mem::size_of_val(self) as libc::socklen_t
            }
        }
    }

    /// Return the available space in the structure
    fn size() -> libc::socklen_t
    where
        Self: Sized,
    {
        mem::size_of::<Self>() as libc::socklen_t
    }

    /// Set the length of this socket address
    ///
    /// This method may only be called on socket addresses whose lengths are dynamic, and it
    /// returns an error if called on a type whose length is static.
    ///
    /// # Safety
    ///
    /// `new_length` must be a valid length for this type of address. Specifically, reads of that
    /// length from `self` must be valid.
    #[doc(hidden)]
    unsafe fn set_length(&mut self, _new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic> {
        Err(SocketAddressLengthNotDynamic)
    }
}

/// The error returned by [`SockaddrLike::set_length`] on an address whose length is statically
/// fixed.
#[derive(Copy, Clone, Debug)]
pub struct SocketAddressLengthNotDynamic;
impl fmt::Display for SocketAddressLengthNotDynamic {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("Attempted to set length on socket whose length is statically fixed")
    }
}
impl std::error::Error for SocketAddressLengthNotDynamic {}

impl private::SockaddrLikePriv for () {
    fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
        ptr::null_mut()
    }
}

/// `()` can be used in place of a real Sockaddr when no address is expected,
/// for example for a field of `Option<S> where S: SockaddrLike`.
// If this RFC ever stabilizes, then ! will be a better choice.
// https://github.com/rust-lang/rust/issues/35121
impl SockaddrLike for () {
    fn as_ptr(&self) -> *const libc::sockaddr {
        ptr::null()
    }

    unsafe fn from_raw(
        _: *const libc::sockaddr,
        _: Option<libc::socklen_t>,
    ) -> Option<Self>
    where
        Self: Sized,
    {
        None
    }

    fn family(&self) -> Option<AddressFamily> {
        None
    }

    fn len(&self) -> libc::socklen_t {
        0
    }
}

/// An IPv4 socket address
#[cfg(feature = "net")]
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct SockaddrIn(libc::sockaddr_in);

#[cfg(feature = "net")]
impl SockaddrIn {
    /// Returns the IP address associated with this socket address, in native
    /// endian.
    pub const fn ip(&self) -> libc::in_addr_t {
        u32::from_be(self.0.sin_addr.s_addr)
    }

    /// Creates a new socket address from IPv4 octets and a port number.
    pub fn new(a: u8, b: u8, c: u8, d: u8, port: u16) -> Self {
        Self(libc::sockaddr_in {
            #[cfg(any(
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "ios",
                target_os = "macos",
                target_os = "netbsd",
                target_os = "aix",
                target_os = "haiku",
                target_os = "openbsd"
            ))]
            sin_len: Self::size() as u8,
            sin_family: AddressFamily::Inet as sa_family_t,
            sin_port: u16::to_be(port),
            sin_addr: libc::in_addr {
                s_addr: u32::from_ne_bytes([a, b, c, d]),
            },
            sin_zero: unsafe { mem::zeroed() },
        })
    }

    /// Returns the port number associated with this socket address, in native
    /// endian.
    pub const fn port(&self) -> u16 {
        u16::from_be(self.0.sin_port)
    }
}

#[cfg(feature = "net")]
impl private::SockaddrLikePriv for SockaddrIn {}
#[cfg(feature = "net")]
impl SockaddrLike for SockaddrIn {
    unsafe fn from_raw(
        addr: *const libc::sockaddr,
        len: Option<libc::socklen_t>,
    ) -> Option<Self>
    where
        Self: Sized,
    {
        if let Some(l) = len {
            if l != mem::size_of::<libc::sockaddr_in>() as libc::socklen_t {
                return None;
            }
        }
        if (*addr).sa_family as i32 != libc::AF_INET {
            return None;
        }
        Some(Self(ptr::read_unaligned(addr as *const _)))
    }
}

#[cfg(feature = "net")]
impl AsRef<libc::sockaddr_in> for SockaddrIn {
    fn as_ref(&self) -> &libc::sockaddr_in {
        &self.0
    }
}

#[cfg(feature = "net")]
impl fmt::Display for SockaddrIn {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let ne = u32::from_be(self.0.sin_addr.s_addr);
        let port = u16::from_be(self.0.sin_port);
        write!(
            f,
            "{}.{}.{}.{}:{}",
            ne >> 24,
            (ne >> 16) & 0xFF,
            (ne >> 8) & 0xFF,
            ne & 0xFF,
            port
        )
    }
}

#[cfg(feature = "net")]
impl From<net::SocketAddrV4> for SockaddrIn {
    fn from(addr: net::SocketAddrV4) -> Self {
        Self(libc::sockaddr_in {
            #[cfg(any(
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "haiku",
                target_os = "hermit",
                target_os = "ios",
                target_os = "macos",
                target_os = "netbsd",
                target_os = "openbsd"
            ))]
            sin_len: mem::size_of::<libc::sockaddr_in>() as u8,
            sin_family: AddressFamily::Inet as sa_family_t,
            sin_port: addr.port().to_be(), // network byte order
            sin_addr: ipv4addr_to_libc(*addr.ip()),
            ..unsafe { mem::zeroed() }
        })
    }
}

#[cfg(feature = "net")]
impl From<SockaddrIn> for net::SocketAddrV4 {
    fn from(addr: SockaddrIn) -> Self {
        net::SocketAddrV4::new(
            net::Ipv4Addr::from(addr.0.sin_addr.s_addr.to_ne_bytes()),
            u16::from_be(addr.0.sin_port),
        )
    }
}

#[cfg(feature = "net")]
impl std::str::FromStr for SockaddrIn {
    type Err = net::AddrParseError;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        net::SocketAddrV4::from_str(s).map(SockaddrIn::from)
    }
}

/// An IPv6 socket address
#[cfg(feature = "net")]
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct SockaddrIn6(libc::sockaddr_in6);

#[cfg(feature = "net")]
impl SockaddrIn6 {
    /// Returns the flow information associated with this address.
    pub const fn flowinfo(&self) -> u32 {
        self.0.sin6_flowinfo
    }

    /// Returns the IP address associated with this socket address.
    pub fn ip(&self) -> net::Ipv6Addr {
        net::Ipv6Addr::from(self.0.sin6_addr.s6_addr)
    }

    /// Returns the port number associated with this socket address, in native
    /// endian.
    pub const fn port(&self) -> u16 {
        u16::from_be(self.0.sin6_port)
    }

    /// Returns the scope ID associated with this address.
    pub const fn scope_id(&self) -> u32 {
        self.0.sin6_scope_id
    }
}

#[cfg(feature = "net")]
impl private::SockaddrLikePriv for SockaddrIn6 {}
#[cfg(feature = "net")]
impl SockaddrLike for SockaddrIn6 {
    unsafe fn from_raw(
        addr: *const libc::sockaddr,
        len: Option<libc::socklen_t>,
    ) -> Option<Self>
    where
        Self: Sized,
    {
        if let Some(l) = len {
            if l != mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t {
                return None;
            }
        }
        if (*addr).sa_family as i32 != libc::AF_INET6 {
            return None;
        }
        Some(Self(ptr::read_unaligned(addr as *const _)))
    }
}

#[cfg(feature = "net")]
impl AsRef<libc::sockaddr_in6> for SockaddrIn6 {
    fn as_ref(&self) -> &libc::sockaddr_in6 {
        &self.0
    }
}

#[cfg(feature = "net")]
impl fmt::Display for SockaddrIn6 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // These things are really hard to display properly.  Easier to let std
        // do it.
        let std = net::SocketAddrV6::new(
            self.ip(),
            self.port(),
            self.flowinfo(),
            self.scope_id(),
        );
        std.fmt(f)
    }
}

#[cfg(feature = "net")]
impl From<net::SocketAddrV6> for SockaddrIn6 {
    fn from(addr: net::SocketAddrV6) -> Self {
        #[allow(clippy::needless_update)] // It isn't needless on Illumos
        Self(libc::sockaddr_in6 {
            #[cfg(any(
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "haiku",
                target_os = "hermit",
                target_os = "ios",
                target_os = "macos",
                target_os = "netbsd",
                target_os = "openbsd"
            ))]
            sin6_len: mem::size_of::<libc::sockaddr_in6>() as u8,
            sin6_family: AddressFamily::Inet6 as sa_family_t,
            sin6_port: addr.port().to_be(), // network byte order
            sin6_addr: ipv6addr_to_libc(addr.ip()),
            sin6_flowinfo: addr.flowinfo(), // host byte order
            sin6_scope_id: addr.scope_id(), // host byte order
            ..unsafe { mem::zeroed() }
        })
    }
}

#[cfg(feature = "net")]
impl From<SockaddrIn6> for net::SocketAddrV6 {
    fn from(addr: SockaddrIn6) -> Self {
        net::SocketAddrV6::new(
            net::Ipv6Addr::from(addr.0.sin6_addr.s6_addr),
            u16::from_be(addr.0.sin6_port),
            addr.0.sin6_flowinfo,
            addr.0.sin6_scope_id,
        )
    }
}

#[cfg(feature = "net")]
impl std::str::FromStr for SockaddrIn6 {
    type Err = net::AddrParseError;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        net::SocketAddrV6::from_str(s).map(SockaddrIn6::from)
    }
}

/// A container for any sockaddr type
///
/// Just like C's `sockaddr_storage`, this type is large enough to hold any type
/// of sockaddr.  It can be used as an argument with functions like
/// [`bind`](super::bind) and [`getsockname`](super::getsockname).  Though it is
/// a union, it can be safely accessed through the `as_*` methods.
///
/// # Example
/// ```
/// # use nix::sys::socket::*;
/// # use std::str::FromStr;
/// # use std::os::unix::io::AsRawFd;
/// let localhost = SockaddrIn::from_str("127.0.0.1:8081").unwrap();
/// let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(),
///     None).unwrap();
/// bind(fd.as_raw_fd(), &localhost).expect("bind");
/// let ss: SockaddrStorage = getsockname(fd.as_raw_fd()).expect("getsockname");
/// assert_eq!(&localhost, ss.as_sockaddr_in().unwrap());
/// ```
#[derive(Clone, Copy, Eq)]
#[repr(C)]
pub union SockaddrStorage {
    #[cfg(any(target_os = "android", target_os = "linux"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    alg: AlgAddr,
    #[cfg(all(feature = "net", not(target_os = "redox")))]
    #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
    dl: LinkAddr,
    #[cfg(any(target_os = "android", target_os = "linux"))]
    nl: NetlinkAddr,
    #[cfg(all(
        feature = "ioctl",
        any(target_os = "ios", target_os = "macos")
    ))]
    #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
    sctl: SysControlAddr,
    #[cfg(feature = "net")]
    sin: SockaddrIn,
    #[cfg(feature = "net")]
    sin6: SockaddrIn6,
    ss: libc::sockaddr_storage,
    su: UnixAddr,
    #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos" ))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    vsock: VsockAddr,
}
impl private::SockaddrLikePriv for SockaddrStorage {}
impl SockaddrLike for SockaddrStorage {
    unsafe fn from_raw(
        addr: *const libc::sockaddr,
        l: Option<libc::socklen_t>,
    ) -> Option<Self>
    where
        Self: Sized,
    {
        if addr.is_null() {
            return None;
        }
        if let Some(len) = l {
            let ulen = len as usize;
            if ulen < offset_of!(libc::sockaddr, sa_data)
                || ulen > mem::size_of::<libc::sockaddr_storage>()
            {
                None
            } else {
                let mut ss: libc::sockaddr_storage = mem::zeroed();
                let ssp = &mut ss as *mut libc::sockaddr_storage as *mut u8;
                ptr::copy(addr as *const u8, ssp, len as usize);
                #[cfg(any(
                    target_os = "android",
                    target_os = "fuchsia",
                    target_os = "illumos",
                    target_os = "linux"
                ))]
                if i32::from(ss.ss_family) == libc::AF_UNIX {
                    // Safe because we UnixAddr is strictly smaller than
                    // SockaddrStorage, and we just initialized the structure.
                    (*(&mut ss as *mut libc::sockaddr_storage
                        as *mut UnixAddr))
                        .sun_len = len as u8;
                }
                Some(Self { ss })
            }
        } else {
            // If length is not available and addr is of a fixed-length type,
            // copy it.  If addr is of a variable length type and len is not
            // available, then there's nothing we can do.
            match (*addr).sa_family as i32 {
                #[cfg(any(target_os = "android", target_os = "linux"))]
                libc::AF_ALG => {
                    AlgAddr::from_raw(addr, l).map(|alg| Self { alg })
                }
                #[cfg(feature = "net")]
                libc::AF_INET => {
                    SockaddrIn::from_raw(addr, l).map(|sin| Self { sin })
                }
                #[cfg(feature = "net")]
                libc::AF_INET6 => {
                    SockaddrIn6::from_raw(addr, l).map(|sin6| Self { sin6 })
                }
                #[cfg(any(
                    target_os = "dragonfly",
                    target_os = "freebsd",
                    target_os = "ios",
                    target_os = "macos",
                    target_os = "illumos",
                    target_os = "netbsd",
                    target_os = "haiku",
                    target_os = "openbsd"
                ))]
                #[cfg(feature = "net")]
                libc::AF_LINK => {
                    LinkAddr::from_raw(addr, l).map(|dl| Self { dl })
                }
                #[cfg(any(target_os = "android", target_os = "linux"))]
                libc::AF_NETLINK => {
                    NetlinkAddr::from_raw(addr, l).map(|nl| Self { nl })
                }
                #[cfg(any(
                    target_os = "android",
                    target_os = "fuchsia",
                    target_os = "linux"
                ))]
                #[cfg(feature = "net")]
                libc::AF_PACKET => {
                    LinkAddr::from_raw(addr, l).map(|dl| Self { dl })
                }
                #[cfg(all(
                    feature = "ioctl",
                    any(target_os = "ios", target_os = "macos")
                ))]
                libc::AF_SYSTEM => {
                    SysControlAddr::from_raw(addr, l).map(|sctl| Self { sctl })
                }
                #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos" ))]
                libc::AF_VSOCK => {
                    VsockAddr::from_raw(addr, l).map(|vsock| Self { vsock })
                }
                _ => None,
            }
        }
    }

    #[cfg(any(
        target_os = "android",
        target_os = "fuchsia",
        target_os = "illumos",
        target_os = "linux"
    ))]
    fn len(&self) -> libc::socklen_t {
        match self.as_unix_addr() {
            // The UnixAddr type knows its own length
            Some(ua) => ua.len(),
            // For all else, we're just a boring SockaddrStorage
            None => mem::size_of_val(self) as libc::socklen_t,
        }
    }

    unsafe fn set_length(&mut self, new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic> {
        match self.as_unix_addr_mut() {
            Some(addr) => {
                addr.set_length(new_length)
            },
            None => Err(SocketAddressLengthNotDynamic),
        }
    }
}

macro_rules! accessors {
    (
        $fname:ident,
        $fname_mut:ident,
        $sockty:ty,
        $family:expr,
        $libc_ty:ty,
        $field:ident) => {
        /// Safely and falliably downcast to an immutable reference
        pub fn $fname(&self) -> Option<&$sockty> {
            if self.family() == Some($family)
                && self.len() >= mem::size_of::<$libc_ty>() as libc::socklen_t
            {
                // Safe because family and len are validated
                Some(unsafe { &self.$field })
            } else {
                None
            }
        }

        /// Safely and falliably downcast to a mutable reference
        pub fn $fname_mut(&mut self) -> Option<&mut $sockty> {
            if self.family() == Some($family)
                && self.len() >= mem::size_of::<$libc_ty>() as libc::socklen_t
            {
                // Safe because family and len are validated
                Some(unsafe { &mut self.$field })
            } else {
                None
            }
        }
    };
}

impl SockaddrStorage {
    /// Downcast to an immutable `[UnixAddr]` reference.
    pub fn as_unix_addr(&self) -> Option<&UnixAddr> {
        cfg_if! {
            if #[cfg(any(target_os = "android",
                     target_os = "fuchsia",
                     target_os = "illumos",
                     target_os = "linux"
                ))]
            {
                let p = unsafe{ &self.ss as *const libc::sockaddr_storage };
                // Safe because UnixAddr is strictly smaller than
                // sockaddr_storage, and we're fully initialized
                let len = unsafe {
                    (*(p as *const UnixAddr )).sun_len as usize
                };
            } else {
                let len = self.len() as usize;
            }
        }
        // Sanity checks
        if self.family() != Some(AddressFamily::Unix)
            || len < offset_of!(libc::sockaddr_un, sun_path)
            || len > mem::size_of::<libc::sockaddr_un>()
        {
            None
        } else {
            Some(unsafe { &self.su })
        }
    }

    /// Downcast to a mutable `[UnixAddr]` reference.
    pub fn as_unix_addr_mut(&mut self) -> Option<&mut UnixAddr> {
        cfg_if! {
            if #[cfg(any(target_os = "android",
                     target_os = "fuchsia",
                     target_os = "illumos",
                     target_os = "linux"
                ))]
            {
                let p = unsafe{ &self.ss as *const libc::sockaddr_storage };
                // Safe because UnixAddr is strictly smaller than
                // sockaddr_storage, and we're fully initialized
                let len = unsafe {
                    (*(p as *const UnixAddr )).sun_len as usize
                };
            } else {
                let len = self.len() as usize;
            }
        }
        // Sanity checks
        if self.family() != Some(AddressFamily::Unix)
            || len < offset_of!(libc::sockaddr_un, sun_path)
            || len > mem::size_of::<libc::sockaddr_un>()
        {
            None
        } else {
            Some(unsafe { &mut self.su })
        }
    }

    #[cfg(any(target_os = "android", target_os = "linux"))]
    accessors! {as_alg_addr, as_alg_addr_mut, AlgAddr,
    AddressFamily::Alg, libc::sockaddr_alg, alg}

    #[cfg(any(
        target_os = "android",
        target_os = "fuchsia",
        target_os = "linux"
    ))]
    #[cfg(feature = "net")]
    accessors! {
    as_link_addr, as_link_addr_mut, LinkAddr,
    AddressFamily::Packet, libc::sockaddr_ll, dl}

    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos",
        target_os = "illumos",
        target_os = "netbsd",
        target_os = "openbsd"
    ))]
    #[cfg(feature = "net")]
    accessors! {
    as_link_addr, as_link_addr_mut, LinkAddr,
    AddressFamily::Link, libc::sockaddr_dl, dl}

    #[cfg(feature = "net")]
    accessors! {
    as_sockaddr_in, as_sockaddr_in_mut, SockaddrIn,
    AddressFamily::Inet, libc::sockaddr_in, sin}

    #[cfg(feature = "net")]
    accessors! {
    as_sockaddr_in6, as_sockaddr_in6_mut, SockaddrIn6,
    AddressFamily::Inet6, libc::sockaddr_in6, sin6}

    #[cfg(any(target_os = "android", target_os = "linux"))]
    accessors! {as_netlink_addr, as_netlink_addr_mut, NetlinkAddr,
    AddressFamily::Netlink, libc::sockaddr_nl, nl}

    #[cfg(all(feature = "ioctl", any(target_os = "ios", target_os = "macos")))]
    #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
    accessors! {as_sys_control_addr, as_sys_control_addr_mut, SysControlAddr,
    AddressFamily::System, libc::sockaddr_ctl, sctl}

    #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
    #[cfg_attr(docsrs, doc(cfg(all())))]
    accessors! {as_vsock_addr, as_vsock_addr_mut, VsockAddr,
    AddressFamily::Vsock, libc::sockaddr_vm, vsock}
}

impl fmt::Debug for SockaddrStorage {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("SockaddrStorage")
            // Safe because sockaddr_storage has the least specific
            // field types
            .field("ss", unsafe { &self.ss })
            .finish()
    }
}

impl fmt::Display for SockaddrStorage {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            match self.ss.ss_family as i32 {
                #[cfg(any(target_os = "android", target_os = "linux"))]
                libc::AF_ALG => self.alg.fmt(f),
                #[cfg(feature = "net")]
                libc::AF_INET => self.sin.fmt(f),
                #[cfg(feature = "net")]
                libc::AF_INET6 => self.sin6.fmt(f),
                #[cfg(any(
                    target_os = "dragonfly",
                    target_os = "freebsd",
                    target_os = "ios",
                    target_os = "macos",
                    target_os = "illumos",
                    target_os = "netbsd",
                    target_os = "openbsd"
                ))]
                #[cfg(feature = "net")]
                libc::AF_LINK => self.dl.fmt(f),
                #[cfg(any(target_os = "android", target_os = "linux"))]
                libc::AF_NETLINK => self.nl.fmt(f),
                #[cfg(any(
                    target_os = "android",
                    target_os = "linux",
                    target_os = "fuchsia"
                ))]
                #[cfg(feature = "net")]
                libc::AF_PACKET => self.dl.fmt(f),
                #[cfg(any(target_os = "ios", target_os = "macos"))]
                #[cfg(feature = "ioctl")]
                libc::AF_SYSTEM => self.sctl.fmt(f),
                libc::AF_UNIX => self.su.fmt(f),
                #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
                libc::AF_VSOCK => self.vsock.fmt(f),
                _ => "<Address family unspecified>".fmt(f),
            }
        }
    }
}

#[cfg(feature = "net")]
impl From<net::SocketAddrV4> for SockaddrStorage {
    fn from(s: net::SocketAddrV4) -> Self {
        unsafe {
            let mut ss: Self = mem::zeroed();
            ss.sin = SockaddrIn::from(s);
            ss
        }
    }
}

#[cfg(feature = "net")]
impl From<net::SocketAddrV6> for SockaddrStorage {
    fn from(s: net::SocketAddrV6) -> Self {
        unsafe {
            let mut ss: Self = mem::zeroed();
            ss.sin6 = SockaddrIn6::from(s);
            ss
        }
    }
}

#[cfg(feature = "net")]
impl From<net::SocketAddr> for SockaddrStorage {
    fn from(s: net::SocketAddr) -> Self {
        match s {
            net::SocketAddr::V4(sa4) => Self::from(sa4),
            net::SocketAddr::V6(sa6) => Self::from(sa6),
        }
    }
}

impl Hash for SockaddrStorage {
    fn hash<H: Hasher>(&self, s: &mut H) {
        unsafe {
            match self.ss.ss_family as i32 {
                #[cfg(any(target_os = "android", target_os = "linux"))]
                libc::AF_ALG => self.alg.hash(s),
                #[cfg(feature = "net")]
                libc::AF_INET => self.sin.hash(s),
                #[cfg(feature = "net")]
                libc::AF_INET6 => self.sin6.hash(s),
                #[cfg(any(
                    target_os = "dragonfly",
                    target_os = "freebsd",
                    target_os = "ios",
                    target_os = "macos",
                    target_os = "illumos",
                    target_os = "netbsd",
                    target_os = "openbsd"
                ))]
                #[cfg(feature = "net")]
                libc::AF_LINK => self.dl.hash(s),
                #[cfg(any(target_os = "android", target_os = "linux"))]
                libc::AF_NETLINK => self.nl.hash(s),
                #[cfg(any(
                    target_os = "android",
                    target_os = "linux",
                    target_os = "fuchsia"
                ))]
                #[cfg(feature = "net")]
                libc::AF_PACKET => self.dl.hash(s),
                #[cfg(any(target_os = "ios", target_os = "macos"))]
                #[cfg(feature = "ioctl")]
                libc::AF_SYSTEM => self.sctl.hash(s),
                libc::AF_UNIX => self.su.hash(s),
                #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
                libc::AF_VSOCK => self.vsock.hash(s),
                _ => self.ss.hash(s),
            }
        }
    }
}

impl PartialEq for SockaddrStorage {
    fn eq(&self, other: &Self) -> bool {
        unsafe {
            match (self.ss.ss_family as i32, other.ss.ss_family as i32) {
                #[cfg(any(target_os = "android", target_os = "linux"))]
                (libc::AF_ALG, libc::AF_ALG) => self.alg == other.alg,
                #[cfg(feature = "net")]
                (libc::AF_INET, libc::AF_INET) => self.sin == other.sin,
                #[cfg(feature = "net")]
                (libc::AF_INET6, libc::AF_INET6) => self.sin6 == other.sin6,
                #[cfg(any(
                    target_os = "dragonfly",
                    target_os = "freebsd",
                    target_os = "ios",
                    target_os = "macos",
                    target_os = "illumos",
                    target_os = "netbsd",
                    target_os = "openbsd"
                ))]
                #[cfg(feature = "net")]
                (libc::AF_LINK, libc::AF_LINK) => self.dl == other.dl,
                #[cfg(any(target_os = "android", target_os = "linux"))]
                (libc::AF_NETLINK, libc::AF_NETLINK) => self.nl == other.nl,
                #[cfg(any(
                    target_os = "android",
                    target_os = "fuchsia",
                    target_os = "linux"
                ))]
                #[cfg(feature = "net")]
                (libc::AF_PACKET, libc::AF_PACKET) => self.dl == other.dl,
                #[cfg(any(target_os = "ios", target_os = "macos"))]
                #[cfg(feature = "ioctl")]
                (libc::AF_SYSTEM, libc::AF_SYSTEM) => self.sctl == other.sctl,
                (libc::AF_UNIX, libc::AF_UNIX) => self.su == other.su,
                #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
                (libc::AF_VSOCK, libc::AF_VSOCK) => self.vsock == other.vsock,
                _ => false,
            }
        }
    }
}

pub(super) mod private {
    pub trait SockaddrLikePriv {
        /// Returns a mutable raw pointer to the inner structure.
        ///
        /// # Safety
        ///
        /// This method is technically safe, but modifying the inner structure's
        /// `family` or `len` fields may result in violating Nix's invariants.
        /// It is best to use this method only with foreign functions that do
        /// not change the sockaddr type.
        fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
            self as *mut Self as *mut libc::sockaddr
        }
    }
}

#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub mod netlink {
    use super::*;
    use crate::sys::socket::addr::AddressFamily;
    use libc::{sa_family_t, sockaddr_nl};
    use std::{fmt, mem};

    /// Address for the Linux kernel user interface device.
    ///
    /// # References
    ///
    /// [netlink(7)](https://man7.org/linux/man-pages/man7/netlink.7.html)
    #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
    #[repr(transparent)]
    pub struct NetlinkAddr(pub(in super::super) sockaddr_nl);

    impl NetlinkAddr {
        /// Construct a new socket address from its port ID and multicast groups
        /// mask.
        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)
        }

        /// Return the socket's port ID.
        pub const fn pid(&self) -> u32 {
            self.0.nl_pid
        }

        /// Return the socket's multicast groups mask
        pub const fn groups(&self) -> u32 {
            self.0.nl_groups
        }
    }

    impl private::SockaddrLikePriv for NetlinkAddr {}
    impl SockaddrLike for NetlinkAddr {
        unsafe fn from_raw(
            addr: *const libc::sockaddr,
            len: Option<libc::socklen_t>,
        ) -> Option<Self>
        where
            Self: Sized,
        {
            if let Some(l) = len {
                if l != mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t {
                    return None;
                }
            }
            if (*addr).sa_family as i32 != libc::AF_NETLINK {
                return None;
            }
            Some(Self(ptr::read_unaligned(addr as *const _)))
        }
    }

    impl AsRef<libc::sockaddr_nl> for NetlinkAddr {
        fn as_ref(&self) -> &libc::sockaddr_nl {
            &self.0
        }
    }

    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"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub mod alg {
    use super::*;
    use libc::{c_char, sockaddr_alg, AF_ALG};
    use std::ffi::CStr;
    use std::hash::{Hash, Hasher};
    use std::{fmt, mem, str};

    /// Socket address for the Linux kernel crypto API
    #[derive(Copy, Clone)]
    #[repr(transparent)]
    pub struct AlgAddr(pub(in super::super) sockaddr_alg);

    impl private::SockaddrLikePriv for AlgAddr {}
    impl SockaddrLike for AlgAddr {
        unsafe fn from_raw(
            addr: *const libc::sockaddr,
            l: Option<libc::socklen_t>,
        ) -> Option<Self>
        where
            Self: Sized,
        {
            if let Some(l) = l {
                if l != mem::size_of::<libc::sockaddr_alg>() as libc::socklen_t
                {
                    return None;
                }
            }
            if (*addr).sa_family as i32 != libc::AF_ALG {
                return None;
            }
            Some(Self(ptr::read_unaligned(addr as *const _)))
        }
    }

    impl AsRef<libc::sockaddr_alg> for AlgAddr {
        fn as_ref(&self) -> &libc::sockaddr_alg {
            &self.0
        }
    }

    // , 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<H: Hasher>(&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 {
        /// Construct an `AF_ALG` socket from its cipher name and type.
        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)
        }

        /// Return the socket's cipher type, for example `hash` or `aead`.
        pub fn alg_type(&self) -> &CStr {
            unsafe {
                CStr::from_ptr(self.0.salg_type.as_ptr() as *const c_char)
            }
        }

        /// Return the socket's cipher name, for example `sha1`.
        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)
        }
    }
}

feature! {
#![feature = "ioctl"]
#[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, ptr};
    use std::os::unix::io::RawFd;
    use crate::{Errno, Result};
    use super::{private, SockaddrLike};

    // 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 = b'N';
    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);

    /// Apple system control socket
    ///
    /// # References
    ///
    /// <https://developer.apple.com/documentation/kernel/sockaddr_ctl>
    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
    #[repr(transparent)]
    pub struct SysControlAddr(pub(in super::super) libc::sockaddr_ctl);

    impl private::SockaddrLikePriv for SysControlAddr {}
    impl SockaddrLike for SysControlAddr {
        unsafe fn from_raw(addr: *const libc::sockaddr, len: Option<libc::socklen_t>)
            -> Option<Self> where Self: Sized
        {
            if let Some(l) = len {
                if l != mem::size_of::<libc::sockaddr_ctl>() as libc::socklen_t {
                    return None;
                }
            }
            if (*addr).sa_family as i32 != libc::AF_SYSTEM {
                return None;
            }
            Some(Self(ptr::read_unaligned(addr as *const _)))
        }
    }

    impl AsRef<libc::sockaddr_ctl> for SysControlAddr {
        fn as_ref(&self) -> &libc::sockaddr_ctl {
            &self.0
        }
    }

    impl SysControlAddr {
        /// Construct a new `SysControlAddr` from its kernel unique identifier
        /// and unit number.
        pub const 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)
        }

        /// Construct a new `SysControlAddr` from its human readable name and
        /// unit number.
        pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result<SysControlAddr> {
            if name.len() > MAX_KCTL_NAME {
                return Err(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))
        }

        /// Return the kernel unique identifier
        pub const fn id(&self) -> u32 {
            self.0.sc_id
        }

        /// Return the kernel controller private unit number.
        pub const 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"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
mod datalink {
    feature! {
    #![feature = "net"]
    use super::{fmt, mem, private, ptr, SockaddrLike};

    /// Hardware Address
    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
    #[repr(transparent)]
    pub struct LinkAddr(pub(in super::super) libc::sockaddr_ll);

    impl LinkAddr {
        /// 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)
        // Returns an Option just for cross-platform compatibility
        pub fn addr(&self) -> Option<[u8; 6]> {
            Some([
                self.0.sll_addr[0],
                self.0.sll_addr[1],
                self.0.sll_addr[2],
                self.0.sll_addr[3],
                self.0.sll_addr[4],
                self.0.sll_addr[5],
            ])
        }
    }

    impl fmt::Display for LinkAddr {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            if let Some(addr) = self.addr() {
                write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
                    addr[0],
                    addr[1],
                    addr[2],
                    addr[3],
                    addr[4],
                    addr[5])
            } else {
                Ok(())
            }
        }
    }
    impl private::SockaddrLikePriv for LinkAddr {}
    impl SockaddrLike for LinkAddr {
        unsafe fn from_raw(addr: *const libc::sockaddr,
                           len: Option<libc::socklen_t>)
            -> Option<Self> where Self: Sized
        {
            if let Some(l) = len {
                if l != mem::size_of::<libc::sockaddr_ll>() as libc::socklen_t {
                    return None;
                }
            }
            if (*addr).sa_family as i32 != libc::AF_PACKET {
                return None;
            }
            Some(Self(ptr::read_unaligned(addr as *const _)))
        }
    }

    impl AsRef<libc::sockaddr_ll> for LinkAddr {
        fn as_ref(&self) -> &libc::sockaddr_ll {
            &self.0
        }
    }

    }
}

#[cfg(any(
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "ios",
    target_os = "macos",
    target_os = "illumos",
    target_os = "netbsd",
    target_os = "haiku",
    target_os = "aix",
    target_os = "openbsd"
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
mod datalink {
    feature! {
    #![feature = "net"]
    use super::{fmt, mem, private, ptr, SockaddrLike};

    /// Hardware Address
    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
    #[repr(transparent)]
    pub struct LinkAddr(pub(in super::super) libc::sockaddr_dl);

    impl LinkAddr {
        /// interface index, if != 0, system given index for interface
        #[cfg(not(target_os = "haiku"))]
        pub fn ifindex(&self) -> usize {
            self.0.sdl_index as usize
        }

        /// Datalink type
        #[cfg(not(target_os = "haiku"))]
        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
        #[cfg(not(target_os = "haiku"))]
        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)
        // The cast is not unnecessary on all platforms.
        #[allow(clippy::unnecessary_cast)]
        pub fn addr(&self) -> Option<[u8; 6]> {
            let nlen = self.nlen();
            let data = self.0.sdl_data;

            if self.is_empty() {
                None
            } else {
                Some([
                    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 {
            if let Some(addr) = self.addr() {
                write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
                    addr[0],
                    addr[1],
                    addr[2],
                    addr[3],
                    addr[4],
                    addr[5])
            } else {
                Ok(())
            }
        }
    }
    impl private::SockaddrLikePriv for LinkAddr {}
    impl SockaddrLike for LinkAddr {
        unsafe fn from_raw(addr: *const libc::sockaddr,
                           len: Option<libc::socklen_t>)
            -> Option<Self> where Self: Sized
        {
            if let Some(l) = len {
                if l != mem::size_of::<libc::sockaddr_dl>() as libc::socklen_t {
                    return None;
                }
            }
            if (*addr).sa_family as i32 != libc::AF_LINK {
                return None;
            }
            Some(Self(ptr::read_unaligned(addr as *const _)))
        }
    }

    impl AsRef<libc::sockaddr_dl> for LinkAddr {
        fn as_ref(&self) -> &libc::sockaddr_dl {
            &self.0
        }
    }
    }
}

#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub mod vsock {
    use super::*;
    use crate::sys::socket::addr::AddressFamily;
    use libc::{sa_family_t, sockaddr_vm};
    use std::hash::{Hash, Hasher};
    use std::{fmt, mem};

    /// Socket address for VMWare VSockets protocol
    ///
    /// # References
    ///
    /// [vsock(7)](https://man7.org/linux/man-pages/man7/vsock.7.html)
    #[derive(Copy, Clone)]
    #[repr(transparent)]
    pub struct VsockAddr(pub(in super::super) sockaddr_vm);

    impl private::SockaddrLikePriv for VsockAddr {}
    impl SockaddrLike for VsockAddr {
        unsafe fn from_raw(
            addr: *const libc::sockaddr,
            len: Option<libc::socklen_t>,
        ) -> Option<Self>
        where
            Self: Sized,
        {
            if let Some(l) = len {
                if l != mem::size_of::<libc::sockaddr_vm>() as libc::socklen_t {
                    return None;
                }
            }
            if (*addr).sa_family as i32 != libc::AF_VSOCK {
                return None;
            }
            Some(Self(ptr::read_unaligned(addr as *const _)))
        }
    }

    impl AsRef<libc::sockaddr_vm> for VsockAddr {
        fn as_ref(&self) -> &libc::sockaddr_vm {
            &self.0
        }
    }

    impl PartialEq for VsockAddr {
        #[cfg(any(target_os = "android", target_os = "linux"))]
        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)
        }
        #[cfg(target_os = "macos")]
        fn eq(&self, other: &Self) -> bool {
            let (inner, other) = (self.0, other.0);
            (inner.svm_family, inner.svm_cid, inner.svm_port, inner.svm_len)
                == (other.svm_family, other.svm_cid, other.svm_port, inner.svm_len)
        }
    }

    impl Eq for VsockAddr {}

    impl Hash for VsockAddr {
        #[cfg(any(target_os = "android", target_os = "linux"))]
        fn hash<H: Hasher>(&self, s: &mut H) {
            let inner = self.0;
            (inner.svm_family, inner.svm_cid, inner.svm_port).hash(s);
        }
        #[cfg(target_os = "macos")]
        fn hash<H: Hasher>(&self, s: &mut H) {
            let inner = self.0;
            (inner.svm_family, inner.svm_cid, inner.svm_port, inner.svm_len).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 {
        /// Construct a `VsockAddr` from its raw fields.
        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;

            #[cfg(target_os = "macos")]
            {
             addr.svm_len =  std::mem::size_of::<sockaddr_vm>() as u8;
            }
            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 {
    use super::*;

    mod types {
        use super::*;

        #[test]
        fn test_ipv4addr_to_libc() {
            let s = std::net::Ipv4Addr::new(1, 2, 3, 4);
            let l = ipv4addr_to_libc(s);
            assert_eq!(l.s_addr, u32::to_be(0x01020304));
        }

        #[test]
        fn test_ipv6addr_to_libc() {
            let s = std::net::Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8);
            let l = ipv6addr_to_libc(&s);
            assert_eq!(
                l.s6_addr,
                [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8]
            );
        }
    }

    #[cfg(not(target_os = "redox"))]
    mod link {
        #![allow(clippy::cast_ptr_alignment)]

        #[cfg(any(
            target_os = "ios",
            target_os = "macos",
            target_os = "illumos"
        ))]
        use super::super::super::socklen_t;
        use super::*;

        /// Don't panic when trying to display an empty datalink address
        #[cfg(any(
            target_os = "dragonfly",
            target_os = "freebsd",
            target_os = "ios",
            target_os = "macos",
            target_os = "netbsd",
            target_os = "openbsd"
        ))]
        #[test]
        fn test_datalink_display() {
            use super::super::LinkAddr;
            use std::mem;

            let la = LinkAddr(libc::sockaddr_dl {
                sdl_len: 56,
                sdl_family: 18,
                sdl_index: 5,
                sdl_type: 24,
                sdl_nlen: 3,
                sdl_alen: 0,
                sdl_slen: 0,
                ..unsafe { mem::zeroed() }
            });
            format!("{la}");
        }

        #[cfg(all(
            any(
                target_os = "android",
                target_os = "fuchsia",
                target_os = "linux"
            ),
            target_endian = "little"
        ))]
        #[test]
        fn linux_loopback() {
            #[repr(align(2))]
            struct Raw([u8; 20]);

            let bytes = Raw([
                17u8, 0, 0, 0, 1, 0, 0, 0, 4, 3, 0, 6, 1, 2, 3, 4, 5, 6, 0, 0,
            ]);
            let sa = bytes.0.as_ptr() as *const libc::sockaddr;
            let len = None;
            let sock_addr =
                unsafe { SockaddrStorage::from_raw(sa, len) }.unwrap();
            assert_eq!(sock_addr.family(), Some(AddressFamily::Packet));
            match sock_addr.as_link_addr() {
                Some(dl) => assert_eq!(dl.addr(), Some([1, 2, 3, 4, 5, 6])),
                None => panic!("Can't unwrap sockaddr storage"),
            }
        }

        #[cfg(any(target_os = "ios", target_os = "macos"))]
        #[test]
        fn macos_loopback() {
            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 len = Some(bytes.len() as socklen_t);
            let sock_addr =
                unsafe { SockaddrStorage::from_raw(sa, len) }.unwrap();
            assert_eq!(sock_addr.family(), Some(AddressFamily::Link));
            match sock_addr.as_link_addr() {
                Some(dl) => {
                    assert!(dl.addr().is_none());
                }
                None => panic!("Can't unwrap sockaddr storage"),
            }
        }

        #[cfg(any(target_os = "ios", target_os = "macos"))]
        #[test]
        fn macos_tap() {
            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 len = Some(bytes.len() as socklen_t);

            let sock_addr =
                unsafe { SockaddrStorage::from_raw(sa, len).unwrap() };
            assert_eq!(sock_addr.family(), Some(AddressFamily::Link));
            match sock_addr.as_link_addr() {
                Some(dl) => {
                    assert_eq!(dl.addr(), Some([24u8, 101, 144, 221, 76, 176]))
                }
                None => panic!("Can't unwrap sockaddr storage"),
            }
        }

        #[cfg(target_os = "illumos")]
        #[test]
        fn illumos_tap() {
            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 len = Some(bytes.len() as socklen_t);
            let _sock_addr = unsafe { SockaddrStorage::from_raw(sa, len) };

            assert!(_sock_addr.is_some());

            let sock_addr = _sock_addr.unwrap();

            assert_eq!(sock_addr.family().unwrap(), AddressFamily::Link);

            assert_eq!(
                sock_addr.as_link_addr().unwrap().addr(),
                Some([24u8, 101, 144, 221, 76, 176])
            );
        }

        #[test]
        fn size() {
            #[cfg(any(
                target_os = "aix",
                target_os = "dragonfly",
                target_os = "freebsd",
                target_os = "ios",
                target_os = "macos",
                target_os = "netbsd",
                target_os = "illumos",
                target_os = "openbsd",
                target_os = "haiku"
            ))]
            let l = mem::size_of::<libc::sockaddr_dl>();
            #[cfg(any(
                target_os = "android",
                target_os = "fuchsia",
                target_os = "linux"
            ))]
            let l = mem::size_of::<libc::sockaddr_ll>();
            assert_eq!(LinkAddr::size() as usize, l);
        }
    }

    mod sockaddr_in {
        use super::*;
        use std::str::FromStr;

        #[test]
        fn display() {
            let s = "127.0.0.1:8080";
            let addr = SockaddrIn::from_str(s).unwrap();
            assert_eq!(s, format!("{addr}"));
        }

        #[test]
        fn size() {
            assert_eq!(
                mem::size_of::<libc::sockaddr_in>(),
                SockaddrIn::size() as usize
            );
        }
    }

    mod sockaddr_in6 {
        use super::*;
        use std::str::FromStr;

        #[test]
        fn display() {
            let s = "[1234:5678:90ab:cdef::1111:2222]:8080";
            let addr = SockaddrIn6::from_str(s).unwrap();
            assert_eq!(s, format!("{addr}"));
        }

        #[test]
        fn size() {
            assert_eq!(
                mem::size_of::<libc::sockaddr_in6>(),
                SockaddrIn6::size() as usize
            );
        }

        #[test]
        // Ensure that we can convert to-and-from std::net variants without change.
        fn to_and_from() {
            let s = "[1234:5678:90ab:cdef::1111:2222]:8080";
            let mut nix_sin6 = SockaddrIn6::from_str(s).unwrap();
            nix_sin6.0.sin6_flowinfo = 0x12345678;
            nix_sin6.0.sin6_scope_id = 0x9abcdef0;

            let std_sin6: std::net::SocketAddrV6 = nix_sin6.into();
            assert_eq!(nix_sin6, std_sin6.into());
        }
    }

    mod sockaddr_storage {
        use super::*;

        #[test]
        fn from_sockaddr_un_named() {
            let ua = UnixAddr::new("/var/run/mysock").unwrap();
            let ptr = ua.as_ptr() as *const libc::sockaddr;
            let ss = unsafe { SockaddrStorage::from_raw(ptr, Some(ua.len())) }
                .unwrap();
            assert_eq!(ss.len(), ua.len());
        }

        #[cfg(any(target_os = "android", target_os = "linux"))]
        #[test]
        fn from_sockaddr_un_abstract_named() {
            let name = String::from("nix\0abstract\0test");
            let ua = UnixAddr::new_abstract(name.as_bytes()).unwrap();
            let ptr = ua.as_ptr() as *const libc::sockaddr;
            let ss = unsafe { SockaddrStorage::from_raw(ptr, Some(ua.len())) }
                .unwrap();
            assert_eq!(ss.len(), ua.len());
        }

        #[cfg(any(target_os = "android", target_os = "linux"))]
        #[test]
        fn from_sockaddr_un_abstract_unnamed() {
            let ua = UnixAddr::new_unnamed();
            let ptr = ua.as_ptr() as *const libc::sockaddr;
            let ss = unsafe { SockaddrStorage::from_raw(ptr, Some(ua.len())) }
                .unwrap();
            assert_eq!(ss.len(), ua.len());
        }
    }

    mod unixaddr {
        use super::*;

        #[cfg(any(target_os = "android", target_os = "linux"))]
        #[test]
        fn abstract_sun_path() {
            let name = String::from("nix\0abstract\0test");
            let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();

            let sun_path1 =
                unsafe { &(*addr.as_ptr()).sun_path[..addr.path_len()] };
            let sun_path2 = [
                0, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0,
                116, 101, 115, 116,
            ];
            assert_eq!(sun_path1, sun_path2);
        }

        #[test]
        fn size() {
            assert_eq!(
                mem::size_of::<libc::sockaddr_un>(),
                UnixAddr::size() as usize
            );
        }
    }
}
