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