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