1 // Copyright 2017 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 //! Used to send and receive messages with file descriptors on sockets that accept control messages
6 //! (e.g. Unix domain sockets).
7
8 use std::fs::File;
9 use std::io;
10 use std::io::IoSlice;
11 use std::io::IoSliceMut;
12 use std::mem::size_of;
13 use std::mem::size_of_val;
14 use std::mem::MaybeUninit;
15 use std::os::unix::io::RawFd;
16 use std::ptr::copy_nonoverlapping;
17 use std::ptr::null_mut;
18 use std::ptr::write_unaligned;
19 use std::slice;
20
21 use libc::c_long;
22 use libc::c_void;
23 use libc::cmsghdr;
24 use libc::iovec;
25 use libc::msghdr;
26 use libc::recvmsg;
27 use libc::SCM_RIGHTS;
28 use libc::SOL_SOCKET;
29 use serde::Deserialize;
30 use serde::Serialize;
31
32 use crate::error;
33 use crate::sys::sendmsg;
34 use crate::AsRawDescriptor;
35 use crate::FromRawDescriptor;
36 use crate::IoBufMut;
37 use crate::RawDescriptor;
38 use crate::SafeDescriptor;
39 use crate::VolatileSlice;
40
41 // Linux kernel limits the max number of file descriptors to be sent at once.
42 pub const SCM_MAX_FD: usize = 253;
43
44 // Each of the following functions performs the same function as their C counterparts. They are
45 // reimplemented as const fns here because they are used to size statically allocated arrays.
46
47 #[allow(non_snake_case)]
CMSG_ALIGN(len: usize) -> usize48 const fn CMSG_ALIGN(len: usize) -> usize {
49 (len + size_of::<c_long>() - 1) & !(size_of::<c_long>() - 1)
50 }
51
52 #[allow(non_snake_case)]
CMSG_SPACE(len: usize) -> usize53 const fn CMSG_SPACE(len: usize) -> usize {
54 size_of::<cmsghdr>() + CMSG_ALIGN(len)
55 }
56
57 #[allow(non_snake_case)]
CMSG_LEN(len: usize) -> usize58 const fn CMSG_LEN(len: usize) -> usize {
59 size_of::<cmsghdr>() + len
60 }
61
62 // This function (macro in the C version) is not used in any compile time constant slots, so is just
63 // an ordinary function. The returned pointer is hard coded to be RawFd because that's all that this
64 // module supports.
65 #[allow(non_snake_case)]
66 #[inline(always)]
CMSG_DATA(cmsg_buffer: *mut cmsghdr) -> *mut RawFd67 fn CMSG_DATA(cmsg_buffer: *mut cmsghdr) -> *mut RawFd {
68 // Essentially returns a pointer to just past the header.
69 cmsg_buffer.wrapping_offset(1) as *mut RawFd
70 }
71
72 // This function is like CMSG_NEXT, but safer because it reads only from references, although it
73 // does some pointer arithmetic on cmsg_ptr.
74 #[allow(clippy::cast_ptr_alignment, clippy::unnecessary_cast)]
get_next_cmsg(msghdr: &msghdr, cmsg: &cmsghdr, cmsg_ptr: *mut cmsghdr) -> *mut cmsghdr75 fn get_next_cmsg(msghdr: &msghdr, cmsg: &cmsghdr, cmsg_ptr: *mut cmsghdr) -> *mut cmsghdr {
76 // The extra cast of cmsg_len to usize is required to build against musl libc, which uses
77 // u32 for cmsg_len.
78 let next_cmsg =
79 (cmsg_ptr as *mut u8).wrapping_add(CMSG_ALIGN(cmsg.cmsg_len as usize)) as *mut cmsghdr;
80 if next_cmsg
81 .wrapping_offset(1)
82 .wrapping_sub(msghdr.msg_control as usize) as usize
83 > msghdr.msg_controllen as usize
84 {
85 null_mut()
86 } else {
87 next_cmsg
88 }
89 }
90
91 const CMSG_BUFFER_INLINE_CAPACITY: usize = CMSG_SPACE(size_of::<RawFd>() * 32);
92
93 enum CmsgBuffer {
94 Inline([u64; (CMSG_BUFFER_INLINE_CAPACITY + 7) / 8]),
95 Heap(Box<[cmsghdr]>),
96 }
97
98 impl CmsgBuffer {
with_capacity(capacity: usize) -> CmsgBuffer99 fn with_capacity(capacity: usize) -> CmsgBuffer {
100 let cap_in_cmsghdr_units =
101 (capacity.checked_add(size_of::<cmsghdr>()).unwrap() - 1) / size_of::<cmsghdr>();
102 if capacity <= CMSG_BUFFER_INLINE_CAPACITY {
103 CmsgBuffer::Inline([0u64; (CMSG_BUFFER_INLINE_CAPACITY + 7) / 8])
104 } else {
105 CmsgBuffer::Heap(
106 vec![
107 // SAFETY:
108 // Safe because cmsghdr only contains primitive types for
109 // which zero initialization is valid.
110 unsafe { MaybeUninit::<cmsghdr>::zeroed().assume_init() };
111 cap_in_cmsghdr_units
112 ]
113 .into_boxed_slice(),
114 )
115 }
116 }
117
as_mut_ptr(&mut self) -> *mut cmsghdr118 fn as_mut_ptr(&mut self) -> *mut cmsghdr {
119 match self {
120 CmsgBuffer::Inline(a) => a.as_mut_ptr() as *mut cmsghdr,
121 CmsgBuffer::Heap(a) => a.as_mut_ptr(),
122 }
123 }
124 }
125
126 // Musl requires a try_into when assigning to msg_iovlen and msg_controllen
127 // that is unnecessary when compiling for glibc.
128 #[allow(clippy::useless_conversion)]
raw_sendmsg(fd: RawFd, iovec: &[iovec], out_fds: &[RawFd]) -> io::Result<usize>129 fn raw_sendmsg(fd: RawFd, iovec: &[iovec], out_fds: &[RawFd]) -> io::Result<usize> {
130 if out_fds.len() > SCM_MAX_FD {
131 error!(
132 "too many fds to send: {} > SCM_MAX_FD (SCM_MAX_FD)",
133 out_fds.len()
134 );
135 return Err(io::Error::from(io::ErrorKind::InvalidInput));
136 }
137
138 let cmsg_capacity = CMSG_SPACE(size_of_val(out_fds));
139 let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
140
141 // SAFETY:
142 // msghdr on musl has private __pad1 and __pad2 fields that cannot be initialized.
143 // Safe because msghdr only contains primitive types for which zero
144 // initialization is valid.
145 let mut msg: msghdr = unsafe { MaybeUninit::zeroed().assume_init() };
146 msg.msg_iov = iovec.as_ptr() as *mut iovec;
147 msg.msg_iovlen = iovec.len().try_into().unwrap();
148
149 if !out_fds.is_empty() {
150 // SAFETY:
151 // msghdr on musl has an extra __pad1 field, initialize the whole struct to zero.
152 // Safe because cmsghdr only contains primitive types for which zero
153 // initialization is valid.
154 let mut cmsg: cmsghdr = unsafe { MaybeUninit::zeroed().assume_init() };
155 cmsg.cmsg_len = CMSG_LEN(size_of_val(out_fds)).try_into().unwrap();
156 cmsg.cmsg_level = SOL_SOCKET;
157 cmsg.cmsg_type = SCM_RIGHTS;
158 // SAFETY: See call specific comments within unsafe block.
159 unsafe {
160 // SAFETY:
161 // Safe because cmsg_buffer was allocated to be large enough to contain cmsghdr.
162 write_unaligned(cmsg_buffer.as_mut_ptr(), cmsg);
163 // SAFETY:
164 // Safe because the cmsg_buffer was allocated to be large enough to hold out_fds.len()
165 // file descriptors.
166 copy_nonoverlapping(
167 out_fds.as_ptr(),
168 CMSG_DATA(cmsg_buffer.as_mut_ptr()),
169 out_fds.len(),
170 );
171 }
172
173 msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
174 msg.msg_controllen = cmsg_capacity.try_into().unwrap();
175 }
176
177 // SAFETY:
178 // Safe because the msghdr was properly constructed from valid (or null) pointers of the
179 // indicated length and we check the return value.
180 let write_count = unsafe { sendmsg(fd, &msg, 0) };
181
182 if write_count == -1 {
183 Err(io::Error::last_os_error())
184 } else {
185 Ok(write_count as usize)
186 }
187 }
188
189 // Musl requires a try_into when assigning to msg_iovlen, msg_controllen and
190 // cmsg_len that is unnecessary when compiling for glibc.
191 #[allow(clippy::useless_conversion, clippy::unnecessary_cast)]
raw_recvmsg( fd: RawFd, iovs: &mut [iovec], max_fds: usize, ) -> io::Result<(usize, Vec<SafeDescriptor>)>192 fn raw_recvmsg(
193 fd: RawFd,
194 iovs: &mut [iovec],
195 max_fds: usize,
196 ) -> io::Result<(usize, Vec<SafeDescriptor>)> {
197 if max_fds > SCM_MAX_FD {
198 error!("too many fds to recieve: {max_fds} > SCM_MAX_FD (SCM_MAX_FD)");
199 return Err(io::Error::from(io::ErrorKind::InvalidInput));
200 }
201
202 let cmsg_capacity = CMSG_SPACE(max_fds * size_of::<RawFd>());
203 let mut cmsg_buffer = CmsgBuffer::with_capacity(cmsg_capacity);
204
205 // SAFETY:
206 // msghdr on musl has private __pad1 and __pad2 fields that cannot be initialized.
207 // Safe because msghdr only contains primitive types for which zero
208 // initialization is valid.
209 let mut msg: msghdr = unsafe { MaybeUninit::zeroed().assume_init() };
210 msg.msg_iov = iovs.as_mut_ptr() as *mut iovec;
211 msg.msg_iovlen = iovs.len().try_into().unwrap();
212
213 if max_fds > 0 {
214 msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void;
215 msg.msg_controllen = cmsg_capacity.try_into().unwrap();
216 }
217
218 // SAFETY:
219 // Safe because the msghdr was properly constructed from valid (or null) pointers of the
220 // indicated length and we check the return value.
221 let total_read = unsafe { recvmsg(fd, &mut msg, 0) };
222
223 if total_read == -1 {
224 return Err(io::Error::last_os_error());
225 }
226
227 if total_read == 0 && (msg.msg_controllen as usize) < size_of::<cmsghdr>() {
228 return Ok((0, Vec::new()));
229 }
230
231 let mut cmsg_ptr = msg.msg_control as *mut cmsghdr;
232 let mut in_fds: Vec<SafeDescriptor> = Vec::with_capacity(max_fds);
233 while !cmsg_ptr.is_null() {
234 // SAFETY:
235 // Safe because we checked that cmsg_ptr was non-null, and the loop is constructed such that
236 // that only happens when there is at least sizeof(cmsghdr) space after the pointer to read.
237 let cmsg = unsafe { (cmsg_ptr as *mut cmsghdr).read_unaligned() };
238
239 if cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_RIGHTS {
240 let fd_count = (cmsg.cmsg_len as usize - CMSG_LEN(0)) / size_of::<RawFd>();
241 let fd_ptr: *const RawFd = CMSG_DATA(cmsg_ptr);
242 for i in 0..fd_count {
243 // SAFETY: `fd_ptr[i]` is within the `CMsgBuffer` allocation.
244 let fd: RawFd = unsafe { fd_ptr.add(i).read_unaligned() };
245 // SAFETY: We own the raw descriptor returned from `recvmsg()`.
246 let sd = unsafe { SafeDescriptor::from_raw_descriptor(fd) };
247 in_fds.push(sd);
248 }
249 }
250
251 cmsg_ptr = get_next_cmsg(&msg, &cmsg, cmsg_ptr);
252 }
253
254 Ok((total_read as usize, in_fds))
255 }
256
257 /// The maximum number of FDs that can be sent in a single send.
258 pub const SCM_SOCKET_MAX_FD_COUNT: usize = 253;
259
260 /// Trait for file descriptors can send and receive socket control messages via `sendmsg` and
261 /// `recvmsg`.
262 ///
263 /// On Linux, this uses MSG_NOSIGNAL to avoid triggering signals. On MacOS, this sets the
264 /// SO_NOSIGPIPE option on the file descriptor to avoid triggering signals.
265 #[derive(Serialize, Deserialize)]
266 pub struct ScmSocket<T: AsRawDescriptor> {
267 pub(in crate::sys) socket: T,
268 }
269
270 impl<T: AsRawDescriptor> ScmSocket<T> {
271 /// Sends the given data and file descriptors over the socket.
272 ///
273 /// On success, returns the number of bytes sent.
274 ///
275 /// The error is constructed via `std::io::Error::last_os_error()`.
276 ///
277 /// # Arguments
278 ///
279 /// * `buf` - A buffer of data to send on the `socket`.
280 /// * `fds` - A list of file descriptors to be sent.
send_with_fds(&self, buf: &[u8], fds: &[RawFd]) -> io::Result<usize>281 pub fn send_with_fds(&self, buf: &[u8], fds: &[RawFd]) -> io::Result<usize> {
282 self.send_vectored_with_fds(&[IoSlice::new(buf)], fds)
283 }
284
285 /// Sends the given data and file descriptors over the socket.
286 ///
287 /// On success, returns the number of bytes sent.
288 ///
289 /// The error is constructed via `std::io::Error::last_os_error()`.
290 ///
291 /// # Arguments
292 ///
293 /// * `bufs` - A slice of buffers of data to send on the `socket`.
294 /// * `fds` - A list of file descriptors to be sent.
send_vectored_with_fds( &self, bufs: &[impl AsIobuf], fds: &[RawFd], ) -> io::Result<usize>295 pub fn send_vectored_with_fds(
296 &self,
297 bufs: &[impl AsIobuf],
298 fds: &[RawFd],
299 ) -> io::Result<usize> {
300 raw_sendmsg(
301 self.socket.as_raw_descriptor(),
302 AsIobuf::as_iobuf_slice(bufs),
303 fds,
304 )
305 }
306
307 /// Receives data and file descriptors from the socket.
308 ///
309 /// On success, returns the number of bytes and file descriptors received as a tuple
310 /// `(bytes count, descriptors)`.
311 ///
312 /// The error is constructed via `std::io::Error::last_os_error()`.
313 ///
314 /// # Arguments
315 ///
316 /// * `buf` - A buffer to store received data.
317 /// * `max_descriptors` - Maximum number of file descriptors to receive.
recv_with_fds( &self, buf: &mut [u8], max_descriptors: usize, ) -> io::Result<(usize, Vec<SafeDescriptor>)>318 pub fn recv_with_fds(
319 &self,
320 buf: &mut [u8],
321 max_descriptors: usize,
322 ) -> io::Result<(usize, Vec<SafeDescriptor>)> {
323 self.recv_vectored_with_fds(&mut [IoSliceMut::new(buf)], max_descriptors)
324 }
325
326 /// Receives data and file descriptors from the socket.
327 ///
328 /// On success, returns the number of bytes and file descriptors received as a tuple
329 /// `(bytes count, files count)`.
330 ///
331 /// The error is constructed via `std::io::Error::last_os_error()`.
332 ///
333 /// # Arguments
334 ///
335 /// * `bufs` - A slice of buffers to store received data.
336 /// * `max_descriptors` - Maximum number of file descriptors to receive.
recv_vectored_with_fds( &self, bufs: &mut [IoSliceMut], max_descriptors: usize, ) -> io::Result<(usize, Vec<SafeDescriptor>)>337 pub fn recv_vectored_with_fds(
338 &self,
339 bufs: &mut [IoSliceMut],
340 max_descriptors: usize,
341 ) -> io::Result<(usize, Vec<SafeDescriptor>)> {
342 raw_recvmsg(
343 self.socket.as_raw_descriptor(),
344 IoSliceMut::as_iobuf_mut_slice(bufs),
345 max_descriptors,
346 )
347 }
348
349 /// Receives data and potentially a file descriptor from the socket.
350 ///
351 /// On success, returns the number of bytes and an optional file descriptor.
352 ///
353 /// The error is constructed via `std::io::Error::last_os_error()`.
354 ///
355 /// # Arguments
356 ///
357 /// * `buf` - A buffer to receive data from the socket.vm
recv_with_file(&self, buf: &mut [u8]) -> io::Result<(usize, Option<File>)>358 pub fn recv_with_file(&self, buf: &mut [u8]) -> io::Result<(usize, Option<File>)> {
359 let (read_count, mut descriptors) = self.recv_with_fds(buf, 1)?;
360 let file = if descriptors.len() == 1 {
361 Some(File::from(descriptors.swap_remove(0)))
362 } else {
363 None
364 };
365 Ok((read_count, file))
366 }
367
368 /// Returns a reference to the wrapped instance.
inner(&self) -> &T369 pub fn inner(&self) -> &T {
370 &self.socket
371 }
372
373 /// Returns a mutable reference to the wrapped instance.
inner_mut(&mut self) -> &mut T374 pub fn inner_mut(&mut self) -> &mut T {
375 &mut self.socket
376 }
377
378 /// Returns the inner object, destroying the ScmSocket.
into_inner(self) -> T379 pub fn into_inner(self) -> T {
380 self.socket
381 }
382 }
383
384 impl<T: AsRawDescriptor> AsRawDescriptor for ScmSocket<T> {
as_raw_descriptor(&self) -> RawDescriptor385 fn as_raw_descriptor(&self) -> RawDescriptor {
386 self.socket.as_raw_descriptor()
387 }
388 }
389
390 /// Trait for types that can be converted into an `iovec` that can be referenced by a syscall for
391 /// the lifetime of this object.
392 ///
393 /// # Safety
394 /// This trait is unsafe because interfaces that use this trait depend on the base pointer and size
395 /// being accurate.
396 pub unsafe trait AsIobuf: Sized {
397 /// Returns a `iovec` that describes a contiguous region of memory.
as_iobuf(&self) -> iovec398 fn as_iobuf(&self) -> iovec;
399
400 /// Returns a slice of `iovec`s that each describe a contiguous region of memory.
401 #[allow(clippy::wrong_self_convention)]
as_iobuf_slice(bufs: &[Self]) -> &[iovec]402 fn as_iobuf_slice(bufs: &[Self]) -> &[iovec];
403
404 /// Returns a mutable slice of `iovecs` that each describe a contiguous region of memory.
as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec]405 fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec];
406 }
407
408 // SAFETY:
409 // Safe because there are no other mutable references to the memory described by `IoSlice` and it is
410 // guaranteed to be ABI-compatible with `iovec`.
411 unsafe impl<'a> AsIobuf for IoSlice<'a> {
as_iobuf(&self) -> iovec412 fn as_iobuf(&self) -> iovec {
413 iovec {
414 iov_base: self.as_ptr() as *mut c_void,
415 iov_len: self.len(),
416 }
417 }
418
as_iobuf_slice(bufs: &[Self]) -> &[iovec]419 fn as_iobuf_slice(bufs: &[Self]) -> &[iovec] {
420 // SAFETY:
421 // Safe because `IoSlice` is guaranteed to be ABI-compatible with `iovec`.
422 unsafe { slice::from_raw_parts(bufs.as_ptr() as *const iovec, bufs.len()) }
423 }
424
as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec]425 fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec] {
426 // SAFETY:
427 // Safe because `IoSlice` is guaranteed to be ABI-compatible with `iovec`.
428 unsafe { slice::from_raw_parts_mut(bufs.as_mut_ptr() as *mut iovec, bufs.len()) }
429 }
430 }
431
432 // SAFETY:
433 // Safe because there are no other references to the memory described by `IoSliceMut` and it is
434 // guaranteed to be ABI-compatible with `iovec`.
435 unsafe impl<'a> AsIobuf for IoSliceMut<'a> {
as_iobuf(&self) -> iovec436 fn as_iobuf(&self) -> iovec {
437 iovec {
438 iov_base: self.as_ptr() as *mut c_void,
439 iov_len: self.len(),
440 }
441 }
442
as_iobuf_slice(bufs: &[Self]) -> &[iovec]443 fn as_iobuf_slice(bufs: &[Self]) -> &[iovec] {
444 // SAFETY:
445 // Safe because `IoSliceMut` is guaranteed to be ABI-compatible with `iovec`.
446 unsafe { slice::from_raw_parts(bufs.as_ptr() as *const iovec, bufs.len()) }
447 }
448
as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec]449 fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec] {
450 // SAFETY:
451 // Safe because `IoSliceMut` is guaranteed to be ABI-compatible with `iovec`.
452 unsafe { slice::from_raw_parts_mut(bufs.as_mut_ptr() as *mut iovec, bufs.len()) }
453 }
454 }
455
456 // SAFETY:
457 // Safe because volatile slices are only ever accessed with other volatile interfaces and the
458 // pointer and size are guaranteed to be accurate.
459 unsafe impl<'a> AsIobuf for VolatileSlice<'a> {
as_iobuf(&self) -> iovec460 fn as_iobuf(&self) -> iovec {
461 *self.as_iobuf().as_ref()
462 }
463
as_iobuf_slice(bufs: &[Self]) -> &[iovec]464 fn as_iobuf_slice(bufs: &[Self]) -> &[iovec] {
465 IoBufMut::as_iobufs(VolatileSlice::as_iobufs(bufs))
466 }
467
as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec]468 fn as_iobuf_mut_slice(bufs: &mut [Self]) -> &mut [iovec] {
469 IoBufMut::as_iobufs_mut(VolatileSlice::as_iobufs_mut(bufs))
470 }
471 }
472
473 #[cfg(test)]
474 #[cfg(any(target_os = "android", target_os = "linux"))] // TODO: eliminate Linux-specific EventExt usage
475 mod tests {
476 use std::io::Write;
477 use std::mem::size_of;
478 use std::os::fd::AsRawFd;
479 use std::os::unix::net::UnixDatagram;
480 use std::slice::from_raw_parts;
481
482 use super::*;
483 use crate::AsRawDescriptor;
484 use crate::Event;
485 use crate::EventExt;
486
487 // Doing this as a macro makes it easier to see the line if it fails
488 macro_rules! CMSG_SPACE_TEST {
489 ($len:literal) => {
490 assert_eq!(
491 CMSG_SPACE(size_of::<[RawFd; $len]>()) as libc::c_uint,
492 // SAFETY: trivially safe
493 unsafe { libc::CMSG_SPACE(size_of::<[RawFd; $len]>() as libc::c_uint) }
494 );
495 };
496 }
497
498 #[test]
499 #[allow(clippy::erasing_op, clippy::identity_op)]
buffer_len()500 fn buffer_len() {
501 CMSG_SPACE_TEST!(0);
502 CMSG_SPACE_TEST!(1);
503 CMSG_SPACE_TEST!(2);
504 CMSG_SPACE_TEST!(3);
505 CMSG_SPACE_TEST!(4);
506 }
507
508 #[test]
send_recv_no_fd()509 fn send_recv_no_fd() {
510 let (u1, u2) = UnixDatagram::pair().expect("failed to create socket pair");
511 let (s1, s2) = (
512 ScmSocket::try_from(u1).unwrap(),
513 ScmSocket::try_from(u2).unwrap(),
514 );
515
516 let send_buf = [1u8, 1, 2, 21, 34, 55];
517 let write_count = s1
518 .send_with_fds(&send_buf, &[])
519 .expect("failed to send data");
520
521 assert_eq!(write_count, 6);
522
523 let mut buf = [0; 6];
524 let (read_count, files) = s2.recv_with_fds(&mut buf, 1).expect("failed to recv data");
525
526 assert_eq!(read_count, 6);
527 assert_eq!(files.len(), 0);
528 assert_eq!(buf, [1, 1, 2, 21, 34, 55]);
529
530 let write_count = s1
531 .send_with_fds(&send_buf, &[])
532 .expect("failed to send data");
533
534 assert_eq!(write_count, 6);
535 let (read_count, files) = s2.recv_with_fds(&mut buf, 1).expect("failed to recv data");
536
537 assert_eq!(read_count, 6);
538 assert_eq!(files.len(), 0);
539 assert_eq!(buf, [1, 1, 2, 21, 34, 55]);
540 }
541
542 #[test]
send_recv_only_fd()543 fn send_recv_only_fd() {
544 let (u1, u2) = UnixDatagram::pair().expect("failed to create socket pair");
545 let (s1, s2) = (
546 ScmSocket::try_from(u1).unwrap(),
547 ScmSocket::try_from(u2).unwrap(),
548 );
549
550 let evt = Event::new().expect("failed to create event");
551 let write_count = s1
552 .send_with_fds(&[], &[evt.as_raw_descriptor()])
553 .expect("failed to send fd");
554
555 assert_eq!(write_count, 0);
556
557 let mut buf = [];
558 let (read_count, file_opt) = s2.recv_with_file(&mut buf).expect("failed to recv fd");
559
560 let mut file = file_opt.unwrap();
561
562 assert_eq!(read_count, 0);
563 assert!(file.as_raw_fd() >= 0);
564 assert_ne!(file.as_raw_fd(), s1.as_raw_descriptor());
565 assert_ne!(file.as_raw_fd(), s2.as_raw_descriptor());
566 assert_ne!(file.as_raw_fd(), evt.as_raw_descriptor());
567
568 // SAFETY: trivially safe
569 file.write_all(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
570 .expect("failed to write to sent fd");
571
572 assert_eq!(evt.read_count().expect("failed to read from event"), 1203);
573 }
574
575 #[test]
send_recv_with_fd()576 fn send_recv_with_fd() {
577 let (u1, u2) = UnixDatagram::pair().expect("failed to create socket pair");
578 let (s1, s2) = (
579 ScmSocket::try_from(u1).unwrap(),
580 ScmSocket::try_from(u2).unwrap(),
581 );
582
583 let evt = Event::new().expect("failed to create event");
584 let write_count = s1
585 .send_with_fds(&[237], &[evt.as_raw_descriptor()])
586 .expect("failed to send fd");
587
588 assert_eq!(write_count, 1);
589
590 let mut buf = [0u8];
591 let (read_count, mut files) = s2.recv_with_fds(&mut buf, 2).expect("failed to recv fd");
592
593 assert_eq!(read_count, 1);
594 assert_eq!(buf[0], 237);
595 assert_eq!(files.len(), 1);
596 assert!(files[0].as_raw_descriptor() >= 0);
597 assert_ne!(files[0].as_raw_descriptor(), s1.as_raw_descriptor());
598 assert_ne!(files[0].as_raw_descriptor(), s2.as_raw_descriptor());
599 assert_ne!(files[0].as_raw_descriptor(), evt.as_raw_descriptor());
600
601 let mut file = File::from(files.swap_remove(0));
602
603 // SAFETY: trivially safe
604 file.write_all(unsafe { from_raw_parts(&1203u64 as *const u64 as *const u8, 8) })
605 .expect("failed to write to sent fd");
606
607 assert_eq!(evt.read_count().expect("failed to read from event"), 1203);
608 }
609 }
610