Skip to content

Commit 43c973a

Browse files
committed
Add support for abstract-name sockets.
In Linux, unix-domain sockets can have an "abstract" address which is encoded with a leading NUL byte. Add support for constructing such abstract addresses. Since `SocketAddrUnix` previously held a `ZString`, which can't accomodate leading NUL bytes, change `SocketAddrUnix` to hold a `sockaddr_un` instead, and redo the encoding/decoding logic to work with it.
1 parent 51421ad commit 43c973a

File tree

9 files changed

+409
-130
lines changed

9 files changed

+409
-130
lines changed

src/imp/libc/net/addr.rs

Lines changed: 197 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,83 +2,258 @@
22
33
use super::super::c;
44
#[cfg(not(windows))]
5-
use crate::ffi::{ZStr, ZString};
5+
use super::offsetof_sun_path;
6+
#[cfg(not(windows))]
7+
use crate::ffi::ZStr;
68
#[cfg(not(windows))]
79
use crate::io;
810
#[cfg(not(windows))]
911
use crate::path;
12+
use core::convert::TryInto;
1013
#[cfg(not(windows))]
1114
use core::fmt;
15+
#[cfg(not(windows))]
16+
use core::mem::transmute;
1217

1318
/// `struct sockaddr_un`
1419
#[cfg(not(windows))]
15-
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
20+
#[derive(Clone)]
1621
#[doc(alias = "sockaddr_un")]
1722
pub struct SocketAddrUnix {
18-
path: ZString,
23+
pub(crate) unix: libc::sockaddr_un,
24+
#[cfg(not(any(
25+
target_os = "dragonfly",
26+
target_os = "freebsd",
27+
target_os = "ios",
28+
target_os = "macos",
29+
target_os = "netbsd",
30+
target_os = "openbsd"
31+
)))]
32+
len: libc::socklen_t,
1933
}
2034

2135
#[cfg(not(windows))]
2236
impl SocketAddrUnix {
23-
/// Construct a new Unix-domain address from a byte slice.
24-
/// filesystem path.
37+
/// Construct a new Unix-domain address from a filesystem path.
2538
#[inline]
2639
pub fn new<P: path::Arg>(path: P) -> io::Result<Self> {
27-
let path = path.into_z_str()?.into_owned();
28-
Self::_new(path)
40+
path.into_with_z_str(|path| Self::_new(path))
2941
}
3042

3143
#[inline]
32-
fn _new(path: ZString) -> io::Result<Self> {
33-
let bytes = path.as_bytes();
44+
fn _new(path: &ZStr) -> io::Result<Self> {
45+
let mut unix = Self::init();
46+
let bytes = path.to_bytes_with_nul();
47+
if bytes.len() > unix.sun_path.len() {
48+
return Err(io::Error::NAMETOOLONG);
49+
}
50+
for (i, b) in bytes.iter().enumerate() {
51+
unix.sun_path[i] = *b as c::c_char;
52+
}
3453

35-
let z = c::sockaddr_un {
36-
#[cfg(any(
54+
#[cfg(any(
55+
target_os = "dragonfly",
56+
target_os = "freebsd",
57+
target_os = "ios",
58+
target_os = "macos",
59+
target_os = "netbsd",
60+
target_os = "openbsd"
61+
))]
62+
{
63+
unix.sun_len = (offsetof_sun_path() + bytes.len()).try_into().unwrap();
64+
}
65+
66+
Ok(Self {
67+
unix,
68+
#[cfg(not(any(
69+
target_os = "dragonfly",
70+
target_os = "freebsd",
71+
target_os = "ios",
72+
target_os = "macos",
73+
target_os = "netbsd",
74+
target_os = "openbsd"
75+
)))]
76+
len: (offsetof_sun_path() + bytes.len()).try_into().unwrap(),
77+
})
78+
}
79+
80+
/// Construct a new abstract Unix-domain address from a byte slice.
81+
#[cfg(any(target_os = "android", target_os = "linux"))]
82+
#[inline]
83+
pub fn new_abstract_name(name: &[u8]) -> io::Result<Self> {
84+
let mut unix = Self::init();
85+
if 1 + name.len() > unix.sun_path.len() {
86+
return Err(io::Error::NAMETOOLONG);
87+
}
88+
unix.sun_path[0] = b'\0' as c::c_char;
89+
for (i, b) in name.iter().enumerate() {
90+
unix.sun_path[1 + i] = *b as c::c_char;
91+
}
92+
let len = offsetof_sun_path() + 1 + name.len();
93+
let len = len.try_into().unwrap();
94+
Ok(Self {
95+
unix,
96+
#[cfg(not(any(
3797
target_os = "dragonfly",
98+
target_os = "freebsd",
3899
target_os = "ios",
100+
target_os = "macos",
101+
target_os = "netbsd",
102+
target_os = "openbsd"
103+
)))]
104+
len,
105+
})
106+
}
107+
108+
fn init() -> c::sockaddr_un {
109+
c::sockaddr_un {
110+
#[cfg(any(
111+
target_os = "dragonfly",
39112
target_os = "freebsd",
113+
target_os = "ios",
40114
target_os = "macos",
41115
target_os = "netbsd",
42116
target_os = "openbsd"
43117
))]
44118
sun_len: 0,
45-
sun_family: 0,
119+
sun_family: c::AF_UNIX as _,
46120
#[cfg(any(
47121
target_os = "dragonfly",
48-
target_os = "ios",
49122
target_os = "freebsd",
123+
target_os = "ios",
50124
target_os = "macos",
51125
target_os = "netbsd",
52126
target_os = "openbsd"
53127
))]
54128
sun_path: [0; 104],
55129
#[cfg(not(any(
56130
target_os = "dragonfly",
57-
target_os = "ios",
58131
target_os = "freebsd",
132+
target_os = "ios",
59133
target_os = "macos",
60134
target_os = "netbsd",
61135
target_os = "openbsd"
62136
)))]
63137
sun_path: [0; 108],
64-
};
65-
if bytes.len() + 1 > z.sun_path.len() {
66-
return Err(io::Error::NAMETOOLONG);
67138
}
68-
Ok(Self { path })
69139
}
70140

71-
/// Returns a reference to the contained path.
141+
/// For a filesystem path address, return the path.
72142
#[inline]
73-
pub fn path(&self) -> &ZStr {
74-
&self.path
143+
pub fn path(&self) -> Option<&ZStr> {
144+
let len = self.len();
145+
if len != 0 && self.unix.sun_path[0] != b'\0' as c::c_char {
146+
let end = len as usize - offsetof_sun_path();
147+
// Safety: Transmuting between `&[c_char]` and `&[u8]`.
148+
unsafe {
149+
Some(ZStr::from_bytes_with_nul(transmute(&self.unix.sun_path[..end])).unwrap())
150+
}
151+
} else {
152+
None
153+
}
154+
}
155+
156+
/// For an abstract address, return the identifier.
157+
#[inline]
158+
#[cfg(any(target_os = "android", target_os = "linux"))]
159+
pub fn abstract_name(&self) -> Option<&[u8]> {
160+
let len = self.len();
161+
if len != 0 && self.unix.sun_path[0] == b'\0' as c::c_char {
162+
let end = len as usize - offsetof_sun_path();
163+
// Safety: Transmuting between `&[c_char]` and `&[u8]`.
164+
unsafe { Some(transmute(&self.unix.sun_path[1..end])) }
165+
} else {
166+
None
167+
}
168+
}
169+
170+
#[inline]
171+
pub(crate) fn addr_len(&self) -> c::socklen_t {
172+
#[cfg(not(any(
173+
target_os = "dragonfly",
174+
target_os = "freebsd",
175+
target_os = "ios",
176+
target_os = "macos",
177+
target_os = "netbsd",
178+
target_os = "openbsd"
179+
)))]
180+
{
181+
self.len
182+
}
183+
#[cfg(any(
184+
target_os = "dragonfly",
185+
target_os = "freebsd",
186+
target_os = "ios",
187+
target_os = "macos",
188+
target_os = "netbsd",
189+
target_os = "openbsd"
190+
))]
191+
{
192+
c::socklen_t::from(self.unix.sun_len)
193+
}
194+
}
195+
196+
#[inline]
197+
pub(crate) fn len(&self) -> usize {
198+
self.addr_len() as usize
199+
}
200+
}
201+
202+
#[cfg(not(windows))]
203+
impl PartialEq for SocketAddrUnix {
204+
#[inline]
205+
fn eq(&self, other: &Self) -> bool {
206+
let self_len = self.len() - offsetof_sun_path();
207+
let other_len = other.len() - offsetof_sun_path();
208+
self.unix.sun_path[..self_len].eq(&other.unix.sun_path[..other_len])
209+
}
210+
}
211+
212+
#[cfg(not(windows))]
213+
impl Eq for SocketAddrUnix {}
214+
215+
#[cfg(not(windows))]
216+
impl PartialOrd for SocketAddrUnix {
217+
#[inline]
218+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
219+
let self_len = self.len() - offsetof_sun_path();
220+
let other_len = other.len() - offsetof_sun_path();
221+
self.unix.sun_path[..self_len].partial_cmp(&other.unix.sun_path[..other_len])
222+
}
223+
}
224+
225+
#[cfg(not(windows))]
226+
impl Ord for SocketAddrUnix {
227+
#[inline]
228+
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
229+
let self_len = self.len() - offsetof_sun_path();
230+
let other_len = other.len() - offsetof_sun_path();
231+
self.unix.sun_path[..self_len].cmp(&other.unix.sun_path[..other_len])
232+
}
233+
}
234+
235+
#[cfg(not(windows))]
236+
impl core::hash::Hash for SocketAddrUnix {
237+
#[inline]
238+
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
239+
let self_len = self.len() - offsetof_sun_path();
240+
self.unix.sun_path[..self_len].hash(state)
75241
}
76242
}
77243

78244
#[cfg(not(windows))]
79245
impl fmt::Debug for SocketAddrUnix {
80246
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
81-
self.path.fmt(fmt)
247+
if let Some(path) = self.path() {
248+
path.fmt(fmt)
249+
} else {
250+
#[cfg(any(target_os = "android", target_os = "linux"))]
251+
if let Some(name) = self.abstract_name() {
252+
return name.fmt(fmt);
253+
}
254+
255+
"(unnamed)".fmt(fmt)
256+
}
82257
}
83258
}
84259

src/imp/libc/net/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ pub use addr::SocketAddrUnix;
1515
pub(crate) use read_sockaddr::{read_sockaddr, read_sockaddr_os};
1616
pub use send_recv::{RecvFlags, SendFlags};
1717
pub use types::{AcceptFlags, AddressFamily, Protocol, Shutdown, SocketFlags, SocketType, Timeout};
18-
#[cfg(not(windows))]
19-
pub(crate) use write_sockaddr::encode_sockaddr_unix;
2018
pub(crate) use write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6, write_sockaddr};
2119

2220
/// Return the offset of the `sun_path` field of `sockaddr_un`.

src/imp/libc/net/syscalls.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use super::super::c;
44
use super::super::conv::{borrowed_fd, ret, ret_owned_fd, ret_send_recv, send_recv_len};
55
use super::ext::{in6_addr_new, in_addr_new};
66
#[cfg(not(windows))]
7-
use super::{encode_sockaddr_unix, SocketAddrUnix};
7+
use super::SocketAddrUnix;
88
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
99
use super::{
1010
encode_sockaddr_v4, encode_sockaddr_v6, read_sockaddr_os, AcceptFlags, AddressFamily, Protocol,
@@ -122,8 +122,8 @@ pub(crate) fn sendto_unix(
122122
buf.as_ptr().cast(),
123123
send_recv_len(buf.len()),
124124
flags.bits(),
125-
as_ptr(&encode_sockaddr_unix(addr)).cast::<c::sockaddr>(),
126-
size_of::<SocketAddrUnix>() as _,
125+
as_ptr(&addr.unix).cast(),
126+
addr.addr_len(),
127127
))?
128128
};
129129
Ok(nwritten as usize)
@@ -187,8 +187,8 @@ pub(crate) fn bind_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Re
187187
unsafe {
188188
ret(c::bind(
189189
borrowed_fd(sockfd),
190-
as_ptr(&encode_sockaddr_unix(addr)).cast(),
191-
size_of::<c::sockaddr_un>() as c::socklen_t,
190+
as_ptr(&addr.unix).cast(),
191+
addr.addr_len(),
192192
))
193193
}
194194
}
@@ -220,8 +220,8 @@ pub(crate) fn connect_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io:
220220
unsafe {
221221
ret(c::connect(
222222
borrowed_fd(sockfd),
223-
as_ptr(&encode_sockaddr_unix(addr)).cast(),
224-
size_of::<c::sockaddr_un>() as c::socklen_t,
223+
as_ptr(&addr.unix).cast(),
224+
addr.addr_len(),
225225
))
226226
}
227227
}

src/imp/libc/net/write_sockaddr.rs

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -90,49 +90,8 @@ unsafe fn write_sockaddr_v6(v6: &SocketAddrV6, storage: *mut SocketAddrStorage)
9090
size_of::<c::sockaddr_in6>()
9191
}
9292

93-
#[cfg(not(windows))]
94-
pub(crate) unsafe fn encode_sockaddr_unix(unix: &SocketAddrUnix) -> c::sockaddr_un {
95-
let mut encoded = c::sockaddr_un {
96-
#[cfg(any(
97-
target_os = "dragonfly",
98-
target_os = "ios",
99-
target_os = "freebsd",
100-
target_os = "macos",
101-
target_os = "netbsd",
102-
target_os = "openbsd"
103-
))]
104-
sun_len: size_of::<c::sockaddr_un>() as _,
105-
sun_family: c::AF_UNIX as _,
106-
#[cfg(any(
107-
target_os = "dragonfly",
108-
target_os = "ios",
109-
target_os = "freebsd",
110-
target_os = "macos",
111-
target_os = "netbsd",
112-
target_os = "openbsd"
113-
))]
114-
sun_path: [0; 104],
115-
#[cfg(not(any(
116-
target_os = "dragonfly",
117-
target_os = "ios",
118-
target_os = "freebsd",
119-
target_os = "macos",
120-
target_os = "netbsd",
121-
target_os = "openbsd"
122-
)))]
123-
sun_path: [0; 108],
124-
};
125-
let bytes = unix.path().to_bytes();
126-
for (i, b) in bytes.iter().enumerate() {
127-
encoded.sun_path[i] = *b as c::c_char;
128-
}
129-
encoded.sun_path[bytes.len()] = b'\0' as c::c_char;
130-
encoded
131-
}
132-
13393
#[cfg(not(windows))]
13494
unsafe fn write_sockaddr_unix(unix: &SocketAddrUnix, storage: *mut SocketAddrStorage) -> usize {
135-
let encoded = encode_sockaddr_unix(unix);
136-
core::ptr::write(storage.cast(), encoded);
137-
super::offsetof_sun_path() + unix.path().to_bytes().len() + 1
95+
core::ptr::write(storage.cast(), unix.unix);
96+
unix.len()
13897
}

0 commit comments

Comments
 (0)