1use crate::ffi::OsStr;
2#[cfg(any(doc, target_os = "android", target_os = "linux"))]
3use crate::os::net::linux_ext;
4use crate::os::unix::ffi::OsStrExt;
5use crate::path::Path;
6use crate::sealed::Sealed;
7use crate::sys::cvt;
8use crate::{fmt, io, mem, ptr};
9
10// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here?
11#[cfg(not(unix))]
12#[allow(non_camel_case_types)]
13mod libc {
14 pub use core::ffi::c_int;
15 pub type socklen_t = u32;
16 pub struct sockaddr;
17 #[derive(Clone)]
18 pub struct sockaddr_un;
19}
20
21fn sun_path_offset(addr: &libc::sockaddr_un) -> usize {
22 // Work with an actual instance of the type since using a null pointer is UB
23 let base: usize = (addr as *const libc::sockaddr_un).addr();
24 let path: usize = core::ptr::addr_of!(addr.sun_path).addr();
25 path - base
26}
27
28pub(super) fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
29 // SAFETY: All zeros is a valid representation for `sockaddr_un`.
30 let mut addr: libc::sockaddr_un = unsafe { mem::zeroed() };
31 addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
32
33 let bytes = path.as_os_str().as_bytes();
34
35 if bytes.contains(&0) {
36 return Err(io::const_io_error!(
37 io::ErrorKind::InvalidInput,
38 "paths must not contain interior null bytes",
39 ));
40 }
41
42 if bytes.len() >= addr.sun_path.len() {
43 return Err(io::const_io_error!(
44 io::ErrorKind::InvalidInput,
45 "path must be shorter than SUN_LEN",
46 ));
47 }
48 // SAFETY: `bytes` and `addr.sun_path` are not overlapping and
49 // both point to valid memory.
50 // NOTE: We zeroed the memory above, so the path is already null
51 // terminated.
52 unsafe {
53 ptr::copy_nonoverlapping(bytes.as_ptr(), addr.sun_path.as_mut_ptr().cast(), bytes.len())
54 };
55
56 let mut len = sun_path_offset(&addr) + bytes.len();
57 match bytes.get(0) {
58 Some(&0) | None => {}
59 Some(_) => len += 1,
60 }
61 Ok((addr, len as libc::socklen_t))
62}
63
64enum AddressKind<'a> {
65 Unnamed,
66 Pathname(&'a Path),
67 Abstract(&'a [u8]),
68}
69
70/// An address associated with a Unix socket.
71///
72/// # Examples
73///
74/// ```
75/// use std::os::unix::net::UnixListener;
76///
77/// let socket = match UnixListener::bind("/tmp/sock") {
78/// Ok(sock) => sock,
79/// Err(e) => {
80/// println!("Couldn't bind: {e:?}");
81/// return
82/// }
83/// };
84/// let addr = socket.local_addr().expect("Couldn't get local address");
85/// ```
86#[derive(Clone)]
87#[stable(feature = "unix_socket", since = "1.10.0")]
88pub struct SocketAddr {
89 pub(super) addr: libc::sockaddr_un,
90 pub(super) len: libc::socklen_t,
91}
92
93impl SocketAddr {
94 pub(super) fn new<F>(f: F) -> io::Result<SocketAddr>
95 where
96 F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int,
97 {
98 unsafe {
99 let mut addr: libc::sockaddr_un = mem::zeroed();
100 let mut len = mem::size_of::<libc::sockaddr_un>() as libc::socklen_t;
101 cvt(f(core::ptr::addr_of_mut!(addr) as *mut _, &mut len))?;
102 SocketAddr::from_parts(addr, len)
103 }
104 }
105
106 pub(super) fn from_parts(
107 addr: libc::sockaddr_un,
108 mut len: libc::socklen_t,
109 ) -> io::Result<SocketAddr> {
110 if cfg!(target_os = "openbsd") {
111 // on OpenBSD, getsockname(2) returns the actual size of the socket address,
112 // and not the len of the content. Figure out the length for ourselves.
113 // https://marc.info/?l=openbsd-bugs&m=170105481926736&w=2
114 let sun_path: &[u8] =
115 unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&addr.sun_path) };
116 len = core::slice::memchr::memchr(0, sun_path)
117 .map_or(len, |new_len| (new_len + sun_path_offset(&addr)) as libc::socklen_t);
118 }
119
120 if len == 0 {
121 // When there is a datagram from unnamed unix socket
122 // linux returns zero bytes of address
123 len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address
124 } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t {
125 return Err(io::const_io_error!(
126 io::ErrorKind::InvalidInput,
127 "file descriptor did not correspond to a Unix socket",
128 ));
129 }
130
131 Ok(SocketAddr { addr, len })
132 }
133
134 /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.
135 ///
136 /// # Errors
137 ///
138 /// Returns an error if the path is longer than `SUN_LEN` or if it contains
139 /// NULL bytes.
140 ///
141 /// # Examples
142 ///
143 /// ```
144 /// use std::os::unix::net::SocketAddr;
145 /// use std::path::Path;
146 ///
147 /// # fn main() -> std::io::Result<()> {
148 /// let address = SocketAddr::from_pathname("/path/to/socket")?;
149 /// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket")));
150 /// # Ok(())
151 /// # }
152 /// ```
153 ///
154 /// Creating a `SocketAddr` with a NULL byte results in an error.
155 ///
156 /// ```
157 /// use std::os::unix::net::SocketAddr;
158 ///
159 /// assert!(SocketAddr::from_pathname("/path/with/\0/bytes").is_err());
160 /// ```
161 #[stable(feature = "unix_socket_creation", since = "1.61.0")]
162 pub fn from_pathname<P>(path: P) -> io::Result<SocketAddr>
163 where
164 P: AsRef<Path>,
165 {
166 sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len })
167 }
168
169 /// Returns `true` if the address is unnamed.
170 ///
171 /// # Examples
172 ///
173 /// A named address:
174 ///
175 /// ```no_run
176 /// use std::os::unix::net::UnixListener;
177 ///
178 /// fn main() -> std::io::Result<()> {
179 /// let socket = UnixListener::bind("/tmp/sock")?;
180 /// let addr = socket.local_addr().expect("Couldn't get local address");
181 /// assert_eq!(addr.is_unnamed(), false);
182 /// Ok(())
183 /// }
184 /// ```
185 ///
186 /// An unnamed address:
187 ///
188 /// ```
189 /// use std::os::unix::net::UnixDatagram;
190 ///
191 /// fn main() -> std::io::Result<()> {
192 /// let socket = UnixDatagram::unbound()?;
193 /// let addr = socket.local_addr().expect("Couldn't get local address");
194 /// assert_eq!(addr.is_unnamed(), true);
195 /// Ok(())
196 /// }
197 /// ```
198 #[must_use]
199 #[stable(feature = "unix_socket", since = "1.10.0")]
200 pub fn is_unnamed(&self) -> bool {
201 matches!(self.address(), AddressKind::Unnamed)
202 }
203
204 /// Returns the contents of this address if it is a `pathname` address.
205 ///
206 /// # Examples
207 ///
208 /// With a pathname:
209 ///
210 /// ```no_run
211 /// use std::os::unix::net::UnixListener;
212 /// use std::path::Path;
213 ///
214 /// fn main() -> std::io::Result<()> {
215 /// let socket = UnixListener::bind("/tmp/sock")?;
216 /// let addr = socket.local_addr().expect("Couldn't get local address");
217 /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock")));
218 /// Ok(())
219 /// }
220 /// ```
221 ///
222 /// Without a pathname:
223 ///
224 /// ```
225 /// use std::os::unix::net::UnixDatagram;
226 ///
227 /// fn main() -> std::io::Result<()> {
228 /// let socket = UnixDatagram::unbound()?;
229 /// let addr = socket.local_addr().expect("Couldn't get local address");
230 /// assert_eq!(addr.as_pathname(), None);
231 /// Ok(())
232 /// }
233 /// ```
234 #[stable(feature = "unix_socket", since = "1.10.0")]
235 #[must_use]
236 pub fn as_pathname(&self) -> Option<&Path> {
237 if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None }
238 }
239
240 fn address(&self) -> AddressKind<'_> {
241 let len = self.len as usize - sun_path_offset(&self.addr);
242 let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) };
243
244 // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses
245 if len == 0
246 || (cfg!(not(any(target_os = "linux", target_os = "android")))
247 && self.addr.sun_path[0] == 0)
248 {
249 AddressKind::Unnamed
250 } else if self.addr.sun_path[0] == 0 {
251 AddressKind::Abstract(&path[1..len])
252 } else {
253 AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
254 }
255 }
256}
257
258#[stable(feature = "unix_socket_abstract", since = "1.70.0")]
259impl Sealed for SocketAddr {}
260
261#[doc(cfg(any(target_os = "android", target_os = "linux")))]
262#[cfg(any(doc, target_os = "android", target_os = "linux"))]
263#[stable(feature = "unix_socket_abstract", since = "1.70.0")]
264impl linux_ext::addr::SocketAddrExt for SocketAddr {
265 fn as_abstract_name(&self) -> Option<&[u8]> {
266 if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None }
267 }
268
269 fn from_abstract_name<N>(name: N) -> crate::io::Result<Self>
270 where
271 N: AsRef<[u8]>,
272 {
273 let name = name.as_ref();
274 unsafe {
275 let mut addr: libc::sockaddr_un = mem::zeroed();
276 addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
277
278 if name.len() + 1 > addr.sun_path.len() {
279 return Err(io::const_io_error!(
280 io::ErrorKind::InvalidInput,
281 "abstract socket name must be shorter than SUN_LEN",
282 ));
283 }
284
285 crate::ptr::copy_nonoverlapping(
286 name.as_ptr(),
287 addr.sun_path.as_mut_ptr().add(1) as *mut u8,
288 name.len(),
289 );
290 let len = (sun_path_offset(&addr) + 1 + name.len()) as libc::socklen_t;
291 SocketAddr::from_parts(addr, len)
292 }
293 }
294}
295
296#[stable(feature = "unix_socket", since = "1.10.0")]
297impl fmt::Debug for SocketAddr {
298 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
299 match self.address() {
300 AddressKind::Unnamed => write!(fmt, "(unnamed)"),
301 AddressKind::Abstract(name: &[u8]) => write!(fmt, "\"{}\" (abstract)", name.escape_ascii()),
302 AddressKind::Pathname(path: &Path) => write!(fmt, "{path:?} (pathname)"),
303 }
304 }
305}
306