cargo / socket2 / audit
cargo : socket2 @ 0.6.3
PE Patrick Elsen signed 2026-05-27 published 2026-05-27

src/sockaddr.rs

654 lines · rust · 2 line annotations

use std::hash::Hash;use std::mem::{self, size_of};use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};#[cfg(not(target_os = "wasi"))]use std::path::Path;use std::{fmt, io, ptr};#[cfg(windows)]use windows_sys::Win32::Networking::WinSock::SOCKADDR_IN6_0;#[cfg(not(target_os = "wasi"))]use crate::sys::AF_UNIX;use crate::sys::{c_int, sockaddr_in, sockaddr_in6, sockaddr_storage, AF_INET, AF_INET6};use crate::Domain;/// The integer type used with `getsockname` on this platform.#[allow(non_camel_case_types)]pub type socklen_t = crate::sys::socklen_t;/// The integer type for the `ss_family` field on this platform.#[allow(non_camel_case_types)]pub type sa_family_t = crate::sys::sa_family_t;/// Rust version of the [`sockaddr_storage`] type.////// This type is intended to be used with with direct calls to the `getsockname` syscall. See the/// documentation of [`SockAddr::new`] for examples.////// This crate defines its own `sockaddr_storage` type to avoid semver concerns with upgrading/// `windows-sys`.#[repr(transparent)]pub struct SockAddrStorage {    storage: sockaddr_storage,}impl SockAddrStorage {    /// Construct a new storage containing all zeros.    #[inline]    pub fn zeroed() -> Self {        // SAFETY: All zeros is valid for this type.        unsafe { mem::zeroed() }    }    /// Returns the size of this storage.    #[inline]    pub fn size_of(&self) -> socklen_t {        size_of::<Self>() as socklen_t    }    /// View this type as another type.    ///    /// # Safety    ///    /// The type `T` must be one of the `sockaddr_*` types defined by this platform.    ///    /// # Examples    /// ```    /// # #[allow(dead_code)]    /// # #[cfg(unix)] mod unix_example {    /// # use core::mem::size_of;    /// use libc::sockaddr_storage;    /// use socket2::{SockAddr, SockAddrStorage, socklen_t};    ///    /// fn from_sockaddr_storage(recv_address: &sockaddr_storage) -> SockAddr {    ///     let mut storage = SockAddrStorage::zeroed();    ///     let libc_address = unsafe { storage.view_as::<sockaddr_storage>() };    ///     *libc_address = *recv_address;    ///     unsafe { SockAddr::new(storage, size_of::<sockaddr_storage>() as socklen_t) }    /// }    /// # }    /// ```    #[inline]
    pub unsafe fn view_as<T>(&mut self) -> &mut T {        assert!(size_of::<T>() <= size_of::<Self>());        // SAFETY: This type is repr(transparent) over `sockaddr_storage` and `T` is one of the        // `sockaddr_*` types defined by this platform.        &mut *(self as *mut Self as *mut T)    }}impl std::fmt::Debug for SockAddrStorage {    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        f.debug_struct("sockaddr_storage")            .field("ss_family", &self.storage.ss_family)            .finish_non_exhaustive()    }}/// The address of a socket.////// `SockAddr`s may be constructed directly to and from the standard library/// [`SocketAddr`], [`SocketAddrV4`], and [`SocketAddrV6`] types.#[derive(Clone)]pub struct SockAddr {    storage: sockaddr_storage,    len: socklen_t,}#[allow(clippy::len_without_is_empty)]impl SockAddr {    /// Create a `SockAddr` from the underlying storage and its length.    ///    /// # Safety    ///    /// Caller must ensure that the address family and length match the type of    /// storage address. For example if `storage.ss_family` is set to `AF_INET`    /// the `storage` must be initialised as `sockaddr_in`, setting the content    /// and length appropriately.    ///    /// # Examples    ///    /// ```    /// # fn main() -> std::io::Result<()> {    /// # #[cfg(unix)] {    /// use std::io;    /// use std::os::fd::AsRawFd;    ///    /// use socket2::{SockAddr, SockAddrStorage, Socket, Domain, Type};    ///    /// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?;    ///    /// // Initialise a `SocketAddr` by calling `getsockname(2)`.    /// let mut addr_storage = SockAddrStorage::zeroed();    /// let mut len = addr_storage.size_of();    ///    /// // The `getsockname(2)` system call will initialize `storage` for    /// // us, setting `len` to the correct length.    /// let res = unsafe {    ///     libc::getsockname(    ///         socket.as_raw_fd(),    ///         addr_storage.view_as(),    ///         &mut len,    ///     )    /// };    /// if res == -1 {    ///     return Err(io::Error::last_os_error());    /// }    ///    /// let address = unsafe { SockAddr::new(addr_storage, len) };    /// # drop(address);    /// # }    /// # Ok(())    /// # }    /// ```    pub const unsafe fn new(storage: SockAddrStorage, len: socklen_t) -> SockAddr {        SockAddr {            storage: storage.storage,            len: len as socklen_t,        }    }    /// Initialise a `SockAddr` by calling the function `init`.    ///    /// The type of the address storage and length passed to the function `init`    /// is OS/architecture specific.    ///    /// The address is zeroed before `init` is called and is thus valid to    /// dereference and read from. The length initialised to the maximum length    /// of the storage.    ///    /// # Safety    ///    /// Caller must ensure that the address family and length match the type of    /// storage address. For example if `storage.ss_family` is set to `AF_INET`    /// the `storage` must be initialised as `sockaddr_in`, setting the content    /// and length appropriately.    ///    /// # Examples    ///    /// ```    /// # fn main() -> std::io::Result<()> {    /// # #[cfg(unix)] {    /// use std::io;    /// use std::os::fd::AsRawFd;    ///    /// use socket2::{SockAddr, Socket, Domain, Type};    ///    /// let socket = Socket::new(Domain::IPV4, Type::STREAM, None)?;    ///    /// // Initialise a `SocketAddr` by calling `getsockname(2)`.    /// let (_, address) = unsafe {    ///     SockAddr::try_init(|addr_storage, len| {    ///         // The `getsockname(2)` system call will initialize `storage` for    ///         // us, setting `len` to the correct length.    ///         if libc::getsockname(socket.as_raw_fd(), addr_storage.cast(), len) == -1 {    ///             Err(io::Error::last_os_error())    ///         } else {    ///             Ok(())    ///         }    ///     })    /// }?;    /// # drop(address);    /// # }    /// # Ok(())    /// # }    /// ```    pub unsafe fn try_init<F, T>(init: F) -> io::Result<(T, SockAddr)>    where        F: FnOnce(*mut SockAddrStorage, *mut socklen_t) -> io::Result<T>,    {        const STORAGE_SIZE: socklen_t = size_of::<sockaddr_storage>() as socklen_t;        // NOTE: `SockAddr::unix` depends on the storage being zeroed before        // calling `init`.        // NOTE: calling `recvfrom` with an empty buffer also depends on the        // storage being zeroed before calling `init` as the OS might not        // initialise it.        let mut storage = SockAddrStorage::zeroed();        let mut len = STORAGE_SIZE;        init(&mut storage, &mut len).map(|res| {            debug_assert!(len <= STORAGE_SIZE, "overflown address storage");            (res, SockAddr::new(storage, len))        })    }
Line 73–213

SockAddr::new and SockAddr::try_init are unsafe fn requiring the caller to ensure ss_family and length match the actual contents. try_init zeroes the storage first (important for recvfrom with zero-length buffers and for Unix sockets), then delegates to a caller-supplied closure. Both functions have clear, accurate safety contracts in their doc comments. as_socket checks ss_family before casting to sockaddr_in / sockaddr_in6; uses ptr::addr_of! to avoid creating references to potentially unaligned/uninit memory. All address-family checks precede the casts. SockAddrStorage::view_as asserts size_of::<T>() <= size_of::<Self>() before the pointer cast.

Supports unsafe-safe, unsafe-documented.

    /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.    ///    /// Returns an error if the path is longer than `SUN_LEN`.    #[cfg(not(target_os = "wasi"))]    pub fn unix<P>(path: P) -> io::Result<SockAddr>    where        P: AsRef<Path>,    {        crate::sys::unix_sockaddr(path.as_ref())    }    /// Set the length of the address.    ///    /// # Safety    ///    /// Caller must ensure that the address up to `length` bytes are properly    /// initialised.    pub unsafe fn set_length(&mut self, length: socklen_t) {        self.len = length;    }    /// Returns this address's family.    pub const fn family(&self) -> sa_family_t {        self.storage.ss_family    }    /// Returns this address's `Domain`.    pub const fn domain(&self) -> Domain {        Domain(self.storage.ss_family as c_int)    }    /// Returns the size of this address in bytes.    pub const fn len(&self) -> socklen_t {        self.len    }    /// Returns a raw pointer to the address.    pub const fn as_ptr(&self) -> *const SockAddrStorage {        &self.storage as *const sockaddr_storage as *const SockAddrStorage    }    /// Returns the address as the storage.    pub const fn as_storage(self) -> SockAddrStorage {        SockAddrStorage {            storage: self.storage,        }    }    /// Returns true if this address is in the `AF_INET` (IPv4) family, false otherwise.    pub const fn is_ipv4(&self) -> bool {        self.storage.ss_family == AF_INET as sa_family_t    }    /// Returns true if this address is in the `AF_INET6` (IPv6) family, false    /// otherwise.    pub const fn is_ipv6(&self) -> bool {        self.storage.ss_family == AF_INET6 as sa_family_t    }    /// Returns true if this address is of a unix socket (for local interprocess communication),    /// i.e. it is from the `AF_UNIX` family, false otherwise.    #[cfg(not(target_os = "wasi"))]    pub fn is_unix(&self) -> bool {        self.storage.ss_family == AF_UNIX as sa_family_t    }    /// Returns this address as a `SocketAddr` if it is in the `AF_INET` (IPv4)    /// or `AF_INET6` (IPv6) family, otherwise returns `None`.    pub fn as_socket(&self) -> Option<SocketAddr> {        if self.storage.ss_family == AF_INET as sa_family_t {            // SAFETY: if the `ss_family` field is `AF_INET` then storage must            // be a `sockaddr_in`.            let addr = unsafe { &*(ptr::addr_of!(self.storage).cast::<sockaddr_in>()) };            let ip = crate::sys::from_in_addr(addr.sin_addr);            let port = u16::from_be(addr.sin_port);            Some(SocketAddr::V4(SocketAddrV4::new(ip, port)))        } else if self.storage.ss_family == AF_INET6 as sa_family_t {            // SAFETY: if the `ss_family` field is `AF_INET6` then storage must            // be a `sockaddr_in6`.            let addr = unsafe { &*(ptr::addr_of!(self.storage).cast::<sockaddr_in6>()) };            let ip = crate::sys::from_in6_addr(addr.sin6_addr);            let port = u16::from_be(addr.sin6_port);            Some(SocketAddr::V6(SocketAddrV6::new(                ip,                port,                addr.sin6_flowinfo,                #[cfg(any(unix, all(target_os = "wasi", not(target_env = "p1"))))]                addr.sin6_scope_id,                #[cfg(windows)]                unsafe {                    addr.Anonymous.sin6_scope_id                },            )))        } else {            None        }    }    /// Returns this address as a [`SocketAddrV4`] if it is in the `AF_INET`    /// family.    pub fn as_socket_ipv4(&self) -> Option<SocketAddrV4> {        match self.as_socket() {            Some(SocketAddr::V4(addr)) => Some(addr),            _ => None,        }    }    /// Returns this address as a [`SocketAddrV6`] if it is in the `AF_INET6`    /// family.    pub fn as_socket_ipv6(&self) -> Option<SocketAddrV6> {        match self.as_socket() {            Some(SocketAddr::V6(addr)) => Some(addr),            _ => None,        }    }    /// Returns the initialised storage bytes.    fn as_bytes(&self) -> &[u8] {        // SAFETY: `self.storage` is a C struct which can always be treated a        // slice of bytes. Furthermore, we ensure we don't read any uninitialised        // bytes by using `self.len`.        unsafe { std::slice::from_raw_parts(self.as_ptr().cast(), self.len as usize) }    }}impl From<SocketAddr> for SockAddr {    fn from(addr: SocketAddr) -> SockAddr {        match addr {            SocketAddr::V4(addr) => addr.into(),            SocketAddr::V6(addr) => addr.into(),        }    }}impl From<SocketAddrV4> for SockAddr {    fn from(addr: SocketAddrV4) -> SockAddr {        // SAFETY: a `sockaddr_storage` of all zeros is valid.        let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };        let len = {            let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::<sockaddr_in>() };            storage.sin_family = AF_INET as sa_family_t;            storage.sin_port = addr.port().to_be();            storage.sin_addr = crate::sys::to_in_addr(addr.ip());            #[cfg(not(target_os = "wasi"))]            {                storage.sin_zero = Default::default();            }            mem::size_of::<sockaddr_in>() as socklen_t        };        #[cfg(any(            target_os = "dragonfly",            target_os = "freebsd",            target_os = "haiku",            target_os = "hermit",            target_os = "ios",            target_os = "visionos",            target_os = "macos",            target_os = "netbsd",            target_os = "nto",            target_os = "openbsd",            target_os = "tvos",            target_os = "vxworks",            target_os = "watchos",        ))]        {            storage.ss_len = len as u8;        }        SockAddr { storage, len }    }}impl From<SocketAddrV6> for SockAddr {    fn from(addr: SocketAddrV6) -> SockAddr {        // SAFETY: a `sockaddr_storage` of all zeros is valid.        let mut storage = unsafe { mem::zeroed::<sockaddr_storage>() };        let len = {            let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::<sockaddr_in6>() };            storage.sin6_family = AF_INET6 as sa_family_t;            storage.sin6_port = addr.port().to_be();            storage.sin6_addr = crate::sys::to_in6_addr(addr.ip());            storage.sin6_flowinfo = addr.flowinfo();            #[cfg(any(unix, all(target_os = "wasi", not(target_env = "p1"))))]            {                storage.sin6_scope_id = addr.scope_id();            }            #[cfg(windows)]            {                storage.Anonymous = SOCKADDR_IN6_0 {                    sin6_scope_id: addr.scope_id(),                };            }            mem::size_of::<sockaddr_in6>() as socklen_t        };        #[cfg(any(            target_os = "dragonfly",            target_os = "freebsd",            target_os = "haiku",            target_os = "hermit",            target_os = "ios",            target_os = "visionos",            target_os = "macos",            target_os = "netbsd",            target_os = "nto",            target_os = "openbsd",            target_os = "tvos",            target_os = "vxworks",            target_os = "watchos",        ))]        {            storage.ss_len = len as u8;        }        SockAddr { storage, len }    }}impl fmt::Debug for SockAddr {    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {        let mut f = fmt.debug_struct("SockAddr");        #[cfg(any(            target_os = "dragonfly",            target_os = "freebsd",            target_os = "haiku",            target_os = "hermit",            target_os = "ios",            target_os = "visionos",            target_os = "macos",            target_os = "netbsd",            target_os = "nto",            target_os = "openbsd",            target_os = "tvos",            target_os = "vxworks",            target_os = "watchos",        ))]        f.field("ss_len", &self.storage.ss_len);        f.field("ss_family", &self.storage.ss_family)            .field("len", &self.len)            .finish()    }}impl PartialEq for SockAddr {    fn eq(&self, other: &Self) -> bool {        self.as_bytes() == other.as_bytes()    }}impl Eq for SockAddr {}impl Hash for SockAddr {    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {        self.as_bytes().hash(state);    }}
#[cfg(test)]mod tests {    use super::*;    #[test]    fn ipv4() {        use std::net::Ipv4Addr;        let std = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);        let addr = SockAddr::from(std);        assert!(addr.is_ipv4());        assert!(!addr.is_ipv6());        #[cfg(not(target_os = "wasi"))]        assert!(!addr.is_unix());        assert_eq!(addr.family(), AF_INET as sa_family_t);        assert_eq!(addr.domain(), Domain::IPV4);        assert_eq!(addr.len(), size_of::<sockaddr_in>() as socklen_t);        assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std)));        assert_eq!(addr.as_socket_ipv4(), Some(std));        assert!(addr.as_socket_ipv6().is_none());        let addr = SockAddr::from(SocketAddr::from(std));        assert_eq!(addr.family(), AF_INET as sa_family_t);        assert_eq!(addr.len(), size_of::<sockaddr_in>() as socklen_t);        assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std)));        assert_eq!(addr.as_socket_ipv4(), Some(std));        assert!(addr.as_socket_ipv6().is_none());        #[cfg(all(unix, not(target_os = "wasi")))]        {            assert!(addr.as_pathname().is_none());            assert!(addr.as_abstract_namespace().is_none());        }    }    #[test]    fn ipv6() {        use std::net::Ipv6Addr;        let std = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);        let addr = SockAddr::from(std);        assert!(addr.is_ipv6());        assert!(!addr.is_ipv4());        #[cfg(not(target_os = "wasi"))]        assert!(!addr.is_unix());        assert_eq!(addr.family(), AF_INET6 as sa_family_t);        assert_eq!(addr.domain(), Domain::IPV6);        assert_eq!(addr.len(), size_of::<sockaddr_in6>() as socklen_t);        assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std)));        assert!(addr.as_socket_ipv4().is_none());        assert_eq!(addr.as_socket_ipv6(), Some(std));        let addr = SockAddr::from(SocketAddr::from(std));        assert_eq!(addr.family(), AF_INET6 as sa_family_t);        assert_eq!(addr.len(), size_of::<sockaddr_in6>() as socklen_t);        assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std)));        assert!(addr.as_socket_ipv4().is_none());        assert_eq!(addr.as_socket_ipv6(), Some(std));        #[cfg(all(unix, not(target_os = "wasi")))]        {            assert!(addr.as_pathname().is_none());            assert!(addr.as_abstract_namespace().is_none());        }    }    #[test]    fn ipv4_eq() {        use std::net::Ipv4Addr;        let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);        let std2 = SocketAddrV4::new(Ipv4Addr::new(5, 6, 7, 8), 8765);        test_eq(            SockAddr::from(std1),            SockAddr::from(std1),            SockAddr::from(std2),        );    }    #[test]    fn ipv4_hash() {        use std::net::Ipv4Addr;        let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);        let std2 = SocketAddrV4::new(Ipv4Addr::new(5, 6, 7, 8), 8765);        test_hash(            SockAddr::from(std1),            SockAddr::from(std1),            SockAddr::from(std2),        );    }    #[test]    fn ipv6_eq() {        use std::net::Ipv6Addr;        let std1 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);        let std2 = SocketAddrV6::new(Ipv6Addr::new(3, 4, 5, 6, 7, 8, 9, 0), 7654, 13, 14);        test_eq(            SockAddr::from(std1),            SockAddr::from(std1),            SockAddr::from(std2),        );    }    #[test]    fn ipv6_hash() {        use std::net::Ipv6Addr;        let std1 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);        let std2 = SocketAddrV6::new(Ipv6Addr::new(3, 4, 5, 6, 7, 8, 9, 0), 7654, 13, 14);        test_hash(            SockAddr::from(std1),            SockAddr::from(std1),            SockAddr::from(std2),        );    }    #[test]    fn ipv4_ipv6_eq() {        use std::net::Ipv4Addr;        use std::net::Ipv6Addr;        let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);        let std2 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);        test_eq(            SockAddr::from(std1),            SockAddr::from(std1),            SockAddr::from(std2),        );        test_eq(            SockAddr::from(std2),            SockAddr::from(std2),            SockAddr::from(std1),        );    }    #[test]    fn ipv4_ipv6_hash() {        use std::net::Ipv4Addr;        use std::net::Ipv6Addr;        let std1 = SocketAddrV4::new(Ipv4Addr::new(1, 2, 3, 4), 9876);        let std2 = SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9876, 11, 12);        test_hash(            SockAddr::from(std1),            SockAddr::from(std1),            SockAddr::from(std2),        );        test_hash(            SockAddr::from(std2),            SockAddr::from(std2),            SockAddr::from(std1),        );    }    #[allow(clippy::eq_op)] // allow a0 == a0 check    fn test_eq(a0: SockAddr, a1: SockAddr, b: SockAddr) {        assert!(a0 == a0);        assert!(a0 == a1);        assert!(a1 == a0);        assert!(a0 != b);        assert!(b != a0);    }    fn test_hash(a0: SockAddr, a1: SockAddr, b: SockAddr) {        assert!(calculate_hash(&a0) == calculate_hash(&a0));        assert!(calculate_hash(&a0) == calculate_hash(&a1));        // technically unequal values can have the same hash, in this case x != z and both have different hashes        assert!(calculate_hash(&a0) != calculate_hash(&b));    }    fn calculate_hash(x: &SockAddr) -> u64 {        use std::collections::hash_map::DefaultHasher;        use std::hash::Hasher;        let mut hasher = DefaultHasher::new();        x.hash(&mut hasher);        hasher.finish()    }}
Line 469–653

The package ships 12 #[test] unit tests in src/sockaddr.rs and src/sys/unix.rs covering IPv4/IPv6 address round-trips, equality, and hashing. The VCS repository contains an additional 60-test integration test suite in tests/socket.rs covering socket creation, binding, options, and platform-specific calls. The integration tests are not included in the published crate. The CI configuration (.github/) runs tests on Linux, macOS, Windows, and multiple targets.

Supports has-unit-tests, has-integration-tests, unsafe-tested.