1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use super::path_offset;
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::{ascii, fmt};

/// An address associated with a `mio` specific Unix socket.
///
/// This is implemented instead of imported from [`net::SocketAddr`] because
/// there is no way to create a [`net::SocketAddr`]. One must be returned by
/// [`accept`], so this is returned instead.
///
/// [`net::SocketAddr`]: std::os::unix::net::SocketAddr
/// [`accept`]: #method.accept
pub struct SocketAddr {
    sockaddr: libc::sockaddr_un,
    socklen: libc::socklen_t,
}

struct AsciiEscaped<'a>(&'a [u8]);

enum AddressKind<'a> {
    Unnamed,
    Pathname(&'a Path),
    Abstract(&'a [u8]),
}

impl SocketAddr {
    fn address(&self) -> AddressKind<'_> {
        let offset = path_offset(&self.sockaddr);
        // Don't underflow in `len` below.
        if (self.socklen as usize) < offset {
            return AddressKind::Unnamed;
        }
        let len = self.socklen as usize - offset;
        let path = unsafe { &*(&self.sockaddr.sun_path as *const [libc::c_char] as *const [u8]) };

        // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses
        if len == 0
            || (cfg!(not(any(target_os = "linux", target_os = "android")))
                && self.sockaddr.sun_path[0] == 0)
        {
            AddressKind::Unnamed
        } else if self.sockaddr.sun_path[0] == 0 {
            AddressKind::Abstract(&path[1..len])
        } else {
            AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
        }
    }
}

cfg_os_poll! {
    use std::{io, mem};

    impl SocketAddr {
        pub(crate) fn new<F>(f: F) -> io::Result<SocketAddr>
        where
            F: FnOnce(*mut libc::sockaddr, &mut libc::socklen_t) -> io::Result<libc::c_int>,
        {
            let mut sockaddr = {
                let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
                unsafe { sockaddr.assume_init() }
            };

            let raw_sockaddr = &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr;
            let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t;

            f(raw_sockaddr, &mut socklen)?;
            Ok(SocketAddr::from_parts(sockaddr, socklen))
        }

        pub(crate) fn from_parts(sockaddr: libc::sockaddr_un, socklen: libc::socklen_t) -> SocketAddr {
            SocketAddr { sockaddr, socklen }
        }

        /// Returns `true` if the address is unnamed.
        ///
        /// Documentation reflected in [`SocketAddr`]
        ///
        /// [`SocketAddr`]: std::os::unix::net::SocketAddr
        // FIXME: The matches macro requires rust 1.42.0 and we still support 1.39.0
        #[allow(clippy::match_like_matches_macro)]
        pub fn is_unnamed(&self) -> bool {
            if let AddressKind::Unnamed = self.address() {
                true
            } else {
                false
            }
        }

        /// Returns the contents of this address if it is a `pathname` address.
        ///
        /// Documentation reflected in [`SocketAddr`]
        ///
        /// [`SocketAddr`]: std::os::unix::net::SocketAddr
        pub fn as_pathname(&self) -> Option<&Path> {
            if let AddressKind::Pathname(path) = self.address() {
                Some(path)
            } else {
                None
            }
        }

        /// Returns the contents of this address if it is an abstract namespace
        /// without the leading null byte.
        // Link to std::os::unix::net::SocketAddr pending
        // https://github.com/rust-lang/rust/issues/85410.
        pub fn as_abstract_namespace(&self) -> Option<&[u8]> {
            if let AddressKind::Abstract(path) = self.address() {
                Some(path)
            } else {
                None
            }
        }
    }
}

impl fmt::Debug for SocketAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.address() {
            AddressKind::Unnamed => write!(fmt, "(unnamed)"),
            AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)),
            AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path),
        }
    }
}

impl<'a> fmt::Display for AsciiEscaped<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(fmt, "\"")?;
        for byte in self.0.iter().cloned().flat_map(ascii::escape_default) {
            write!(fmt, "{}", byte as char)?;
        }
        write!(fmt, "\"")
    }
}