1use std::io;
31use std::os::unix::net::UnixStream;
32use std::os::unix::prelude::*;
33
34mod constants;
35mod util;
36
37#[cfg(any(target_os = "linux", target_os = "openbsd", target_os = "netbsd"))]
38pub mod ucred;
39#[cfg(any(
40 target_os = "freebsd",
41 target_os = "dragonfly",
42 target_os = "macos",
43 target_os = "ios"
44))]
45pub mod xucred;
46
47#[allow(clippy::needless_return)]
48#[inline]
49unsafe fn get_peer_ids_raw(sockfd: RawFd) -> io::Result<(libc::uid_t, libc::gid_t)> {
50 #[cfg(any(target_os = "linux", target_os = "openbsd", target_os = "netbsd"))]
51 {
52 let cred = ucred::get_ucred_raw(sockfd)?;
53 return Ok((cred.uid, cred.gid));
54 }
55
56 #[cfg(any(
57 target_os = "freebsd",
58 target_os = "dragonfly",
59 target_os = "macos",
60 target_os = "ios"
61 ))]
62 {
63 let cred = xucred::get_xucred_raw(sockfd)?;
64 return Ok((cred.uid(), cred.gid()));
65 }
66}
67
68#[inline]
70pub fn get_peer_ids(sock: &UnixStream) -> io::Result<(libc::uid_t, libc::gid_t)> {
71 unsafe { get_peer_ids_raw(sock.as_raw_fd()) }
72}
73
74#[cfg(any(
75 target_os = "linux",
76 target_os = "openbsd",
77 target_os = "netbsd",
78 target_os = "freebsd",
79))]
80#[allow(clippy::needless_return)]
81#[inline]
82unsafe fn get_peer_pid_ids_raw(
83 sockfd: RawFd,
84) -> io::Result<(Option<libc::pid_t>, libc::uid_t, libc::gid_t)> {
85 #[cfg(any(target_os = "linux", target_os = "openbsd", target_os = "netbsd"))]
86 {
87 let cred = ucred::get_ucred_raw(sockfd)?;
88 return Ok((Some(cred.pid), cred.uid, cred.gid));
89 }
90
91 #[cfg(target_os = "freebsd")]
92 {
93 let cred = xucred::get_xucred_raw(sockfd)?;
94 return Ok((cred.pid(), cred.uid(), cred.gid()));
95 }
96}
97
98#[cfg(any(
103 target_os = "linux",
104 target_os = "openbsd",
105 target_os = "netbsd",
106 target_os = "freebsd",
107))]
108#[inline]
109pub fn get_peer_pid_ids(
110 sock: &UnixStream,
111) -> io::Result<(Option<libc::pid_t>, libc::uid_t, libc::gid_t)> {
112 unsafe { get_peer_pid_ids_raw(sock.as_raw_fd()) }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_get_peer_ids() {
121 let (a, b) = UnixStream::pair().unwrap();
122
123 let (auid, agid) = get_peer_ids(&a).unwrap();
124 assert_eq!(auid, unsafe { libc::getuid() });
125 assert_eq!(agid, unsafe { libc::getgid() });
126
127 let (buid, bgid) = get_peer_ids(&b).unwrap();
128 assert_eq!(buid, unsafe { libc::getuid() });
129 assert_eq!(bgid, unsafe { libc::getgid() });
130 }
131
132 #[test]
133 fn test_get_peer_ids_bad_fd() {
134 assert_eq!(
135 get_peer_ids(unsafe { &UnixStream::from_raw_fd(libc::c_int::MAX) })
136 .unwrap_err()
137 .raw_os_error(),
138 Some(libc::EBADF),
139 );
140
141 let file = std::fs::File::open(std::env::current_exe().unwrap()).unwrap();
142 assert_eq!(
143 get_peer_ids(unsafe { &UnixStream::from_raw_fd(file.as_raw_fd()) })
144 .unwrap_err()
145 .raw_os_error(),
146 Some(libc::ENOTSOCK),
147 );
148 }
149
150 #[cfg(any(
151 target_os = "linux",
152 target_os = "openbsd",
153 target_os = "netbsd",
154 target_os = "freebsd",
155 ))]
156 #[test]
157 fn test_get_peer_pid_ids() {
158 let (a, b) = UnixStream::pair().unwrap();
159
160 let (apid, auid, agid) = get_peer_pid_ids(&a).unwrap();
161 assert_eq!(apid, get_expected_pid());
162 assert_eq!(auid, unsafe { libc::getuid() });
163 assert_eq!(agid, unsafe { libc::getgid() });
164
165 let (bpid, buid, bgid) = get_peer_pid_ids(&b).unwrap();
166 assert_eq!(bpid, get_expected_pid());
167 assert_eq!(buid, unsafe { libc::getuid() });
168 assert_eq!(bgid, unsafe { libc::getgid() });
169 }
170
171 #[cfg(any(
172 target_os = "linux",
173 target_os = "openbsd",
174 target_os = "netbsd",
175 target_os = "freebsd",
176 ))]
177 #[test]
178 fn test_get_peer_pid_ids_bad_fd() {
179 assert_eq!(
180 get_peer_pid_ids(unsafe { &UnixStream::from_raw_fd(libc::c_int::MAX) })
181 .unwrap_err()
182 .raw_os_error(),
183 Some(libc::EBADF),
184 );
185
186 let file = std::fs::File::open(std::env::current_exe().unwrap()).unwrap();
187 assert_eq!(
188 get_peer_pid_ids(unsafe { &UnixStream::from_raw_fd(file.as_raw_fd()) })
189 .unwrap_err()
190 .raw_os_error(),
191 Some(libc::ENOTSOCK),
192 );
193 }
194
195 #[cfg(any(
196 target_os = "linux",
197 target_os = "openbsd",
198 target_os = "netbsd",
199 target_os = "freebsd",
200 ))]
201 fn get_expected_pid() -> Option<libc::pid_t> {
202 #[cfg(target_os = "freebsd")]
203 if !util::has_cr_pid() {
204 return None;
205 }
206
207 Some(unsafe { libc::getpid() })
208 }
209}