Skip to main content

posix_socket/address/
unix.rs

1use crate::{AsSocketAddress, SpecificSocketAddress};
2use std::path::Path;
3
4/// Unix socket address.
5///
6/// A Unix socket address can be unnamed or a filesystem path.
7/// On Linux it can also be an abstract socket path, although this is not portable.
8#[derive(Clone)]
9#[repr(C)]
10pub struct UnixSocketAddress {
11	/// The inner C-compatible socket address.
12	inner: libc::sockaddr_un,
13
14	/// The length of the socket address.
15	len: libc::socklen_t,
16}
17
18impl UnixSocketAddress {
19	/// Create a Unix socket address from a path.
20	pub fn new<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
21		use std::os::unix::ffi::OsStrExt;
22		let path = path.as_ref().as_os_str().as_bytes();
23
24		unsafe {
25			let mut output = Self {
26				inner: libc::sockaddr_un {
27					sun_family: Self::static_family(),
28					sun_path: std::mem::zeroed(),
29				},
30				len: 0,
31			};
32			let path_offset = output.path_offset();
33			if path.len() >= Self::max_len() as usize - path_offset - 1 {
34				Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "path is too large for a socket address"))
35			} else if path.is_empty() {
36				Ok(output)
37			} else {
38				std::ptr::copy(
39					path.as_ptr(),
40					output.inner.sun_path.as_mut_ptr() as *mut u8,
41					path.len(),
42				);
43				output.len = (path_offset + path.len() + 1) as libc::socklen_t;
44				Ok(output)
45			}
46		}
47	}
48
49	/// Create a new unnamed unix socket address.
50	pub fn new_unnamed() -> Self {
51		unsafe {
52			let mut address = Self {
53				inner: libc::sockaddr_un {
54					sun_family: Self::static_family(),
55					sun_path: std::mem::zeroed(),
56				},
57				len: 0,
58			};
59			address.len = address.path_offset() as libc::socklen_t;
60			address
61		}
62	}
63
64	/// Create a Unix socket address from a [`libc::sockaddr_un`] and a length.
65	pub fn from_raw(inner: libc::sockaddr_un, len: libc::socklen_t) -> Self {
66		Self { inner, len }
67	}
68
69	/// Convert the [`SocketAddress`] into raw [`libc`] parts.
70	pub fn into_raw(self) -> (libc::sockaddr_un, libc::socklen_t) {
71		(self.inner, self.len)
72	}
73
74	/// Get the path associated with the socket address, if there is one.
75	///
76	/// Returns [`None`] if the socket address is unnamed or abstract,
77	pub fn as_path(&self) -> Option<&Path> {
78		unsafe {
79			use std::os::unix::ffi::OsStrExt;
80			let path_len = self.path_len();
81			if path_len == 0 {
82				None
83			} else if self.inner.sun_path[0] == 0 {
84				None
85			} else {
86				let path: &[u8] = std::mem::transmute(&self.inner.sun_path[..path_len - 1]);
87				let path = std::ffi::OsStr::from_bytes(path);
88				Some(Path::new(path))
89			}
90		}
91	}
92
93	/// Check if the address is unnamed.
94	pub fn is_unnamed(&self) -> bool {
95		self.path_len() == 0
96	}
97
98	/// Get the abstract path associated with the socket address.
99	///
100	/// Returns [`None`] if the socket address is not abstract.
101	///
102	/// Abstract Unix socket addresses are a non-portable Linux extension.
103	pub fn as_abstract(&self) -> Option<&std::ffi::CStr> {
104		unsafe {
105			let path_len = self.path_len();
106			if path_len > 0 && self.inner.sun_path[0] == 0 {
107				Some(std::mem::transmute(&self.inner.sun_path[1..path_len]))
108			} else {
109				None
110			}
111		}
112	}
113
114	/// Get the offset of the path within the [`libc::sockaddr_un`] struct.
115	fn path_offset(&self) -> usize {
116		let start = &self.inner as *const _ as usize;
117		let sun_path = &self.inner.sun_path as *const _ as usize;
118		sun_path - start
119	}
120
121	/// Get the length of the path portion of the address including the terminating null byte.
122	fn path_len(&self) -> usize {
123		self.len() as usize - self.path_offset()
124	}
125}
126
127impl SpecificSocketAddress for UnixSocketAddress {
128	fn static_family() -> libc::sa_family_t {
129		libc::AF_LOCAL as libc::sa_family_t
130	}
131}
132
133unsafe impl AsSocketAddress for UnixSocketAddress {
134	fn as_sockaddr(&self) -> *const libc::sockaddr {
135		&self.inner as *const _ as *const _
136	}
137
138	fn as_sockaddr_mut(address: &mut std::mem::MaybeUninit<Self>) -> *mut libc::sockaddr {
139		unsafe { &mut address.as_mut_ptr().as_mut().unwrap().inner as *mut _ as *mut _ }
140	}
141
142	fn len(&self) -> libc::socklen_t {
143		self.len
144	}
145
146	fn finalize(address: std::mem::MaybeUninit<Self>, len: libc::socklen_t) -> std::io::Result<Self> {
147		unsafe {
148			let mut address = address.assume_init();
149			if address.family() != Self::static_family() {
150				return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "wrong address family, expeced AF_LOCAL"));
151			}
152			if len > Self::max_len() {
153				return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "address too large"));
154			}
155			address.len = len;
156			Ok(address)
157		}
158	}
159
160	fn max_len() -> libc::socklen_t {
161		std::mem::size_of::<libc::sockaddr_un>() as libc::socklen_t
162	}
163}
164
165impl From<UnixSocketAddress> for crate::SocketAddress {
166	fn from(other: UnixSocketAddress) -> Self {
167		Self::from(&other)
168	}
169}
170
171impl From<&UnixSocketAddress> for crate::SocketAddress {
172	fn from(other: &UnixSocketAddress) -> Self {
173		Self::from_other(other)
174	}
175}
176
177impl From<std::os::unix::net::SocketAddr> for UnixSocketAddress {
178	fn from(other: std::os::unix::net::SocketAddr) -> Self {
179		Self::from(&other)
180	}
181}
182
183impl From<&std::os::unix::net::SocketAddr> for UnixSocketAddress {
184	fn from(other: &std::os::unix::net::SocketAddr) -> Self {
185		if let Some(path) = other.as_pathname() {
186			Self::new(path).unwrap()
187		} else if other.is_unnamed() {
188			Self::new_unnamed()
189		} else {
190			panic!("attempted to convert an std::unix::net::SocketAddr that is not a path and not unnamed");
191		}
192	}
193}