1use super::{SocketAddr, sockaddr_un};
4use crate::io::{self, IoSlice, IoSliceMut};
5use crate::marker::PhantomData;
6use crate::mem::zeroed;
7use crate::os::unix::io::RawFd;
8use crate::path::Path;
9use crate::ptr::{eq, read_unaligned};
10use crate::slice::from_raw_parts;
11use crate::sys::net::Socket;
12
13#[cfg(all(
15    doc,
16    not(target_os = "linux"),
17    not(target_os = "android"),
18    not(target_os = "netbsd"),
19    not(target_os = "freebsd")
20))]
21#[allow(non_camel_case_types)]
22mod libc {
23    pub use core::ffi::c_int;
24    pub struct ucred;
25    pub struct cmsghdr;
26    pub struct sockcred2;
27    pub type pid_t = i32;
28    pub type gid_t = u32;
29    pub type uid_t = u32;
30}
31
32pub(super) fn recv_vectored_with_ancillary_from(
33    socket: &Socket,
34    bufs: &mut [IoSliceMut<'_>],
35    ancillary: &mut SocketAncillary<'_>,
36) -> io::Result<(usize, bool, io::Result<SocketAddr>)> {
37    unsafe {
38        let mut msg_name: libc::sockaddr_un = zeroed();
39        let mut msg: libc::msghdr = zeroed();
40        msg.msg_name = (&raw mut msg_name) as *mut _;
41        msg.msg_namelen = size_of::<libc::sockaddr_un>() as libc::socklen_t;
42        msg.msg_iov = bufs.as_mut_ptr().cast();
43        msg.msg_iovlen = bufs.len() as _;
44        msg.msg_controllen = ancillary.buffer.len() as _;
45        if msg.msg_controllen > 0 {
47            msg.msg_control = ancillary.buffer.as_mut_ptr().cast();
48        }
49
50        let count = socket.recv_msg(&mut msg)?;
51
52        ancillary.length = msg.msg_controllen as usize;
53        ancillary.truncated = msg.msg_flags & libc::MSG_CTRUNC == libc::MSG_CTRUNC;
54
55        let truncated = msg.msg_flags & libc::MSG_TRUNC == libc::MSG_TRUNC;
56        let addr = SocketAddr::from_parts(msg_name, msg.msg_namelen);
57
58        Ok((count, truncated, addr))
59    }
60}
61
62pub(super) fn send_vectored_with_ancillary_to(
63    socket: &Socket,
64    path: Option<&Path>,
65    bufs: &[IoSlice<'_>],
66    ancillary: &mut SocketAncillary<'_>,
67) -> io::Result<usize> {
68    unsafe {
69        let (mut msg_name, msg_namelen) =
70            if let Some(path) = path { sockaddr_un(path)? } else { (zeroed(), 0) };
71
72        let mut msg: libc::msghdr = zeroed();
73        msg.msg_name = (&raw mut msg_name) as *mut _;
74        msg.msg_namelen = msg_namelen;
75        msg.msg_iov = bufs.as_ptr() as *mut _;
76        msg.msg_iovlen = bufs.len() as _;
77        msg.msg_controllen = ancillary.length as _;
78        if msg.msg_controllen > 0 {
80            msg.msg_control = ancillary.buffer.as_mut_ptr().cast();
81        }
82
83        ancillary.truncated = false;
84
85        socket.send_msg(&mut msg)
86    }
87}
88
89fn add_to_ancillary_data<T>(
90    buffer: &mut [u8],
91    length: &mut usize,
92    source: &[T],
93    cmsg_level: libc::c_int,
94    cmsg_type: libc::c_int,
95) -> bool {
96    #[cfg(not(target_os = "freebsd"))]
97    let cmsg_size = source.len().checked_mul(size_of::<T>());
98    #[cfg(target_os = "freebsd")]
99    let cmsg_size = Some(unsafe { libc::SOCKCRED2SIZE(1) });
100
101    let source_len = if let Some(source_len) = cmsg_size {
102        if let Ok(source_len) = u32::try_from(source_len) {
103            source_len
104        } else {
105            return false;
106        }
107    } else {
108        return false;
109    };
110
111    unsafe {
112        let additional_space = libc::CMSG_SPACE(source_len) as usize;
113
114        let new_length = if let Some(new_length) = additional_space.checked_add(*length) {
115            new_length
116        } else {
117            return false;
118        };
119
120        if new_length > buffer.len() {
121            return false;
122        }
123
124        buffer[*length..new_length].fill(0);
125
126        *length = new_length;
127
128        let mut msg: libc::msghdr = zeroed();
129        msg.msg_control = buffer.as_mut_ptr().cast();
130        msg.msg_controllen = *length as _;
131
132        let mut cmsg = libc::CMSG_FIRSTHDR(&msg);
133        let mut previous_cmsg = cmsg;
134        while !cmsg.is_null() {
135            previous_cmsg = cmsg;
136            cmsg = libc::CMSG_NXTHDR(&msg, cmsg);
137
138            if eq(cmsg, previous_cmsg) {
142                break;
143            }
144        }
145
146        if previous_cmsg.is_null() {
147            return false;
148        }
149
150        (*previous_cmsg).cmsg_level = cmsg_level;
151        (*previous_cmsg).cmsg_type = cmsg_type;
152        (*previous_cmsg).cmsg_len = libc::CMSG_LEN(source_len) as _;
153
154        let data = libc::CMSG_DATA(previous_cmsg).cast();
155
156        libc::memcpy(data, source.as_ptr().cast(), source_len as usize);
157    }
158    true
159}
160
161struct AncillaryDataIter<'a, T> {
162    data: &'a [u8],
163    phantom: PhantomData<T>,
164}
165
166impl<'a, T> AncillaryDataIter<'a, T> {
167    unsafe fn new(data: &'a [u8]) -> AncillaryDataIter<'a, T> {
173        AncillaryDataIter { data, phantom: PhantomData }
174    }
175}
176
177impl<'a, T> Iterator for AncillaryDataIter<'a, T> {
178    type Item = T;
179
180    fn next(&mut self) -> Option<T> {
181        if size_of::<T>() <= self.data.len() {
182            unsafe {
183                let unit = read_unaligned(self.data.as_ptr().cast());
184                self.data = &self.data[size_of::<T>()..];
185                Some(unit)
186            }
187        } else {
188            None
189        }
190    }
191}
192
193#[cfg(all(
194    doc,
195    not(target_os = "android"),
196    not(target_os = "linux"),
197    not(target_os = "netbsd"),
198    not(target_os = "freebsd")
199))]
200#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
201#[derive(Clone)]
202pub struct SocketCred(());
203
204#[cfg(any(target_os = "android", target_os = "linux",))]
206#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
207#[derive(Clone)]
208pub struct SocketCred(libc::ucred);
209
210#[cfg(target_os = "netbsd")]
211#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
212#[derive(Clone)]
213pub struct SocketCred(libc::sockcred);
214
215#[cfg(target_os = "freebsd")]
216#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
217#[derive(Clone)]
218pub struct SocketCred(libc::sockcred2);
219
220#[doc(cfg(any(target_os = "android", target_os = "linux")))]
221#[cfg(any(target_os = "android", target_os = "linux"))]
222impl SocketCred {
223    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
227    #[must_use]
228    pub fn new() -> SocketCred {
229        SocketCred(libc::ucred { pid: 0, uid: 0, gid: 0 })
230    }
231
232    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
234    pub fn set_pid(&mut self, pid: libc::pid_t) {
235        self.0.pid = pid;
236    }
237
238    #[must_use]
240    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
241    pub fn get_pid(&self) -> libc::pid_t {
242        self.0.pid
243    }
244
245    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
247    pub fn set_uid(&mut self, uid: libc::uid_t) {
248        self.0.uid = uid;
249    }
250
251    #[must_use]
253    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
254    pub fn get_uid(&self) -> libc::uid_t {
255        self.0.uid
256    }
257
258    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
260    pub fn set_gid(&mut self, gid: libc::gid_t) {
261        self.0.gid = gid;
262    }
263
264    #[must_use]
266    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
267    pub fn get_gid(&self) -> libc::gid_t {
268        self.0.gid
269    }
270}
271
272#[cfg(target_os = "freebsd")]
273impl SocketCred {
274    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
278    #[must_use]
279    pub fn new() -> SocketCred {
280        SocketCred(libc::sockcred2 {
281            sc_version: 0,
282            sc_pid: 0,
283            sc_uid: 0,
284            sc_euid: 0,
285            sc_gid: 0,
286            sc_egid: 0,
287            sc_ngroups: 0,
288            sc_groups: [0; 1],
289        })
290    }
291
292    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
294    pub fn set_pid(&mut self, pid: libc::pid_t) {
295        self.0.sc_pid = pid;
296    }
297
298    #[must_use]
300    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
301    pub fn get_pid(&self) -> libc::pid_t {
302        self.0.sc_pid
303    }
304
305    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
307    pub fn set_uid(&mut self, uid: libc::uid_t) {
308        self.0.sc_euid = uid;
309    }
310
311    #[must_use]
313    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
314    pub fn get_uid(&self) -> libc::uid_t {
315        self.0.sc_euid
316    }
317
318    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
320    pub fn set_gid(&mut self, gid: libc::gid_t) {
321        self.0.sc_egid = gid;
322    }
323
324    #[must_use]
326    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
327    pub fn get_gid(&self) -> libc::gid_t {
328        self.0.sc_egid
329    }
330}
331
332#[cfg(target_os = "netbsd")]
333impl SocketCred {
334    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
338    pub fn new() -> SocketCred {
339        SocketCred(libc::sockcred {
340            sc_pid: 0,
341            sc_uid: 0,
342            sc_euid: 0,
343            sc_gid: 0,
344            sc_egid: 0,
345            sc_ngroups: 0,
346            sc_groups: [0u32; 1],
347        })
348    }
349
350    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
352    pub fn set_pid(&mut self, pid: libc::pid_t) {
353        self.0.sc_pid = pid;
354    }
355
356    #[must_use]
358    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
359    pub fn get_pid(&self) -> libc::pid_t {
360        self.0.sc_pid
361    }
362
363    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
365    pub fn set_uid(&mut self, uid: libc::uid_t) {
366        self.0.sc_uid = uid;
367    }
368
369    #[must_use]
371    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
372    pub fn get_uid(&self) -> libc::uid_t {
373        self.0.sc_uid
374    }
375
376    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
378    pub fn set_gid(&mut self, gid: libc::gid_t) {
379        self.0.sc_gid = gid;
380    }
381
382    #[must_use]
384    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
385    pub fn get_gid(&self) -> libc::gid_t {
386        self.0.sc_gid
387    }
388}
389
390#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
394pub struct ScmRights<'a>(AncillaryDataIter<'a, RawFd>);
395
396#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
397impl<'a> Iterator for ScmRights<'a> {
398    type Item = RawFd;
399
400    fn next(&mut self) -> Option<RawFd> {
401        self.0.next()
402    }
403}
404
405#[cfg(all(
406    doc,
407    not(target_os = "android"),
408    not(target_os = "linux"),
409    not(target_os = "netbsd"),
410    not(target_os = "freebsd")
411))]
412#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
413pub struct ScmCredentials<'a>(AncillaryDataIter<'a, ()>);
414
415#[cfg(any(target_os = "android", target_os = "linux",))]
419#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
420pub struct ScmCredentials<'a>(AncillaryDataIter<'a, libc::ucred>);
421
422#[cfg(target_os = "freebsd")]
423#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
424pub struct ScmCredentials<'a>(AncillaryDataIter<'a, libc::sockcred2>);
425
426#[cfg(target_os = "netbsd")]
427#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
428pub struct ScmCredentials<'a>(AncillaryDataIter<'a, libc::sockcred>);
429
430#[cfg(any(
431    doc,
432    target_os = "android",
433    target_os = "linux",
434    target_os = "netbsd",
435    target_os = "freebsd"
436))]
437#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
438impl<'a> Iterator for ScmCredentials<'a> {
439    type Item = SocketCred;
440
441    fn next(&mut self) -> Option<SocketCred> {
442        Some(SocketCred(self.0.next()?))
443    }
444}
445
446#[non_exhaustive]
448#[derive(Debug)]
449#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
450pub enum AncillaryError {
451    Unknown { cmsg_level: i32, cmsg_type: i32 },
452}
453
454#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
456pub enum AncillaryData<'a> {
457    ScmRights(ScmRights<'a>),
458    #[cfg(any(
459        doc,
460        target_os = "android",
461        target_os = "linux",
462        target_os = "netbsd",
463        target_os = "freebsd"
464    ))]
465    ScmCredentials(ScmCredentials<'a>),
466}
467
468impl<'a> AncillaryData<'a> {
469    unsafe fn as_rights(data: &'a [u8]) -> Self {
476        let ancillary_data_iter = AncillaryDataIter::new(data);
477        let scm_rights = ScmRights(ancillary_data_iter);
478        AncillaryData::ScmRights(scm_rights)
479    }
480
481    #[cfg(any(
488        doc,
489        target_os = "android",
490        target_os = "linux",
491        target_os = "netbsd",
492        target_os = "freebsd"
493    ))]
494    unsafe fn as_credentials(data: &'a [u8]) -> Self {
495        let ancillary_data_iter = AncillaryDataIter::new(data);
496        let scm_credentials = ScmCredentials(ancillary_data_iter);
497        AncillaryData::ScmCredentials(scm_credentials)
498    }
499
500    fn try_from_cmsghdr(cmsg: &'a libc::cmsghdr) -> Result<Self, AncillaryError> {
501        unsafe {
502            let cmsg_len_zero = libc::CMSG_LEN(0) as usize;
503            let data_len = (*cmsg).cmsg_len as usize - cmsg_len_zero;
504            let data = libc::CMSG_DATA(cmsg).cast();
505            let data = from_raw_parts(data, data_len);
506
507            match (*cmsg).cmsg_level {
508                libc::SOL_SOCKET => match (*cmsg).cmsg_type {
509                    libc::SCM_RIGHTS => Ok(AncillaryData::as_rights(data)),
510                    #[cfg(any(target_os = "android", target_os = "linux",))]
511                    libc::SCM_CREDENTIALS => Ok(AncillaryData::as_credentials(data)),
512                    #[cfg(target_os = "freebsd")]
513                    libc::SCM_CREDS2 => Ok(AncillaryData::as_credentials(data)),
514                    #[cfg(target_os = "netbsd")]
515                    libc::SCM_CREDS => Ok(AncillaryData::as_credentials(data)),
516                    cmsg_type => {
517                        Err(AncillaryError::Unknown { cmsg_level: libc::SOL_SOCKET, cmsg_type })
518                    }
519                },
520                cmsg_level => {
521                    Err(AncillaryError::Unknown { cmsg_level, cmsg_type: (*cmsg).cmsg_type })
522                }
523            }
524        }
525    }
526}
527
528#[must_use = "iterators are lazy and do nothing unless consumed"]
530#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
531pub struct Messages<'a> {
532    buffer: &'a [u8],
533    current: Option<&'a libc::cmsghdr>,
534}
535
536#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
537impl<'a> Iterator for Messages<'a> {
538    type Item = Result<AncillaryData<'a>, AncillaryError>;
539
540    fn next(&mut self) -> Option<Self::Item> {
541        unsafe {
542            let mut msg: libc::msghdr = zeroed();
543            msg.msg_control = self.buffer.as_ptr() as *mut _;
544            msg.msg_controllen = self.buffer.len() as _;
545
546            let cmsg = if let Some(current) = self.current {
547                libc::CMSG_NXTHDR(&msg, current)
548            } else {
549                libc::CMSG_FIRSTHDR(&msg)
550            };
551
552            let cmsg = cmsg.as_ref()?;
553
554            if let Some(current) = self.current {
558                if eq(current, cmsg) {
559                    return None;
560                }
561            }
562
563            self.current = Some(cmsg);
564            let ancillary_result = AncillaryData::try_from_cmsghdr(cmsg);
565            Some(ancillary_result)
566        }
567    }
568}
569
570#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
600#[derive(Debug)]
601pub struct SocketAncillary<'a> {
602    buffer: &'a mut [u8],
603    length: usize,
604    truncated: bool,
605}
606
607impl<'a> SocketAncillary<'a> {
608    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
620    pub fn new(buffer: &'a mut [u8]) -> Self {
621        SocketAncillary { buffer, length: 0, truncated: false }
622    }
623
624    #[must_use]
626    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
627    pub fn capacity(&self) -> usize {
628        self.buffer.len()
629    }
630
631    #[must_use]
633    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
634    pub fn is_empty(&self) -> bool {
635        self.length == 0
636    }
637
638    #[must_use]
640    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
641    pub fn len(&self) -> usize {
642        self.length
643    }
644
645    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
647    pub fn messages(&self) -> Messages<'_> {
648        Messages { buffer: &self.buffer[..self.length], current: None }
649    }
650
651    #[must_use]
675    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
676    pub fn truncated(&self) -> bool {
677        self.truncated
678    }
679
680    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
709    pub fn add_fds(&mut self, fds: &[RawFd]) -> bool {
710        self.truncated = false;
711        add_to_ancillary_data(
712            &mut self.buffer,
713            &mut self.length,
714            fds,
715            libc::SOL_SOCKET,
716            libc::SCM_RIGHTS,
717        )
718    }
719
720    #[cfg(any(
728        doc,
729        target_os = "android",
730        target_os = "linux",
731        target_os = "netbsd",
732        target_os = "freebsd"
733    ))]
734    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
735    pub fn add_creds(&mut self, creds: &[SocketCred]) -> bool {
736        self.truncated = false;
737        add_to_ancillary_data(
738            &mut self.buffer,
739            &mut self.length,
740            creds,
741            libc::SOL_SOCKET,
742            #[cfg(not(any(target_os = "netbsd", target_os = "freebsd")))]
743            libc::SCM_CREDENTIALS,
744            #[cfg(target_os = "freebsd")]
745            libc::SCM_CREDS2,
746            #[cfg(target_os = "netbsd")]
747            libc::SCM_CREDS,
748        )
749    }
750
751    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
794    pub fn clear(&mut self) {
795        self.length = 0;
796        self.truncated = false;
797    }
798}