1 | //! Socket interface functions |
2 | //! |
3 | //! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html) |
4 | #[cfg (target_os = "linux" )] |
5 | #[cfg (feature = "uio" )] |
6 | use crate::sys::time::TimeSpec; |
7 | #[cfg (feature = "uio" )] |
8 | use crate::sys::time::TimeVal; |
9 | use crate::{errno::Errno, Result}; |
10 | use cfg_if::cfg_if; |
11 | use libc::{ |
12 | self, c_int, c_void, iovec, size_t, socklen_t, CMSG_DATA, CMSG_FIRSTHDR, |
13 | CMSG_LEN, CMSG_NXTHDR, |
14 | }; |
15 | use std::convert::{TryFrom, TryInto}; |
16 | use std::io::{IoSlice, IoSliceMut}; |
17 | #[cfg (feature = "net" )] |
18 | use std::net; |
19 | use std::os::unix::io::RawFd; |
20 | use std::{mem, ptr, slice}; |
21 | |
22 | #[deny (missing_docs)] |
23 | mod addr; |
24 | #[deny (missing_docs)] |
25 | pub mod sockopt; |
26 | |
27 | /* |
28 | * |
29 | * ===== Re-exports ===== |
30 | * |
31 | */ |
32 | |
33 | pub use self::addr::{SockaddrLike, SockaddrStorage}; |
34 | |
35 | #[cfg (not(any(target_os = "illumos" , target_os = "solaris" )))] |
36 | #[allow (deprecated)] |
37 | pub use self::addr::{AddressFamily, SockAddr, UnixAddr}; |
38 | #[cfg (any(target_os = "illumos" , target_os = "solaris" ))] |
39 | #[allow (deprecated)] |
40 | pub use self::addr::{AddressFamily, SockAddr, UnixAddr}; |
41 | #[allow (deprecated)] |
42 | #[cfg (not(any( |
43 | target_os = "illumos" , |
44 | target_os = "solaris" , |
45 | target_os = "haiku" |
46 | )))] |
47 | #[cfg (feature = "net" )] |
48 | pub use self::addr::{ |
49 | InetAddr, IpAddr, Ipv4Addr, Ipv6Addr, LinkAddr, SockaddrIn, SockaddrIn6, |
50 | }; |
51 | #[allow (deprecated)] |
52 | #[cfg (any( |
53 | target_os = "illumos" , |
54 | target_os = "solaris" , |
55 | target_os = "haiku" |
56 | ))] |
57 | #[cfg (feature = "net" )] |
58 | pub use self::addr::{ |
59 | InetAddr, IpAddr, Ipv4Addr, Ipv6Addr, SockaddrIn, SockaddrIn6, |
60 | }; |
61 | |
62 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
63 | pub use crate::sys::socket::addr::alg::AlgAddr; |
64 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
65 | pub use crate::sys::socket::addr::netlink::NetlinkAddr; |
66 | #[cfg (any(target_os = "ios" , target_os = "macos" ))] |
67 | #[cfg (feature = "ioctl" )] |
68 | pub use crate::sys::socket::addr::sys_control::SysControlAddr; |
69 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
70 | pub use crate::sys::socket::addr::vsock::VsockAddr; |
71 | |
72 | #[cfg (feature = "uio" )] |
73 | pub use libc::{cmsghdr, msghdr}; |
74 | pub use libc::{sa_family_t, sockaddr, sockaddr_storage, sockaddr_un}; |
75 | #[cfg (feature = "net" )] |
76 | pub use libc::{sockaddr_in, sockaddr_in6}; |
77 | |
78 | // Needed by the cmsg_space macro |
79 | #[doc (hidden)] |
80 | pub use libc::{c_uint, CMSG_SPACE}; |
81 | |
82 | #[cfg (feature = "net" )] |
83 | use crate::sys::socket::addr::{ipv4addr_to_libc, ipv6addr_to_libc}; |
84 | |
85 | /// These constants are used to specify the communication semantics |
86 | /// when creating a socket with [`socket()`](fn.socket.html) |
87 | #[derive (Clone, Copy, PartialEq, Eq, Debug)] |
88 | #[repr (i32)] |
89 | #[non_exhaustive ] |
90 | pub enum SockType { |
91 | /// Provides sequenced, reliable, two-way, connection- |
92 | /// based byte streams. An out-of-band data transmission |
93 | /// mechanism may be supported. |
94 | Stream = libc::SOCK_STREAM, |
95 | /// Supports datagrams (connectionless, unreliable |
96 | /// messages of a fixed maximum length). |
97 | Datagram = libc::SOCK_DGRAM, |
98 | /// Provides a sequenced, reliable, two-way connection- |
99 | /// based data transmission path for datagrams of fixed |
100 | /// maximum length; a consumer is required to read an |
101 | /// entire packet with each input system call. |
102 | SeqPacket = libc::SOCK_SEQPACKET, |
103 | /// Provides raw network protocol access. |
104 | Raw = libc::SOCK_RAW, |
105 | /// Provides a reliable datagram layer that does not |
106 | /// guarantee ordering. |
107 | #[cfg (not(any(target_os = "haiku" )))] |
108 | Rdm = libc::SOCK_RDM, |
109 | } |
110 | // The TryFrom impl could've been derived using libc_enum!. But for |
111 | // backwards-compatibility with Nix-0.25.0 we manually implement it, so as to |
112 | // keep the old variant names. |
113 | impl TryFrom<i32> for SockType { |
114 | type Error = crate::Error; |
115 | |
116 | fn try_from(x: i32) -> Result<Self> { |
117 | match x { |
118 | libc::SOCK_STREAM => Ok(Self::Stream), |
119 | libc::SOCK_DGRAM => Ok(Self::Datagram), |
120 | libc::SOCK_SEQPACKET => Ok(Self::SeqPacket), |
121 | libc::SOCK_RAW => Ok(Self::Raw), |
122 | #[cfg (not(any(target_os = "haiku" )))] |
123 | libc::SOCK_RDM => Ok(Self::Rdm), |
124 | _ => Err(Errno::EINVAL) |
125 | } |
126 | } |
127 | } |
128 | |
129 | /// Constants used in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html) |
130 | /// to specify the protocol to use. |
131 | #[repr (i32)] |
132 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
133 | #[non_exhaustive ] |
134 | pub enum SockProtocol { |
135 | /// TCP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html)) |
136 | Tcp = libc::IPPROTO_TCP, |
137 | /// UDP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html)) |
138 | Udp = libc::IPPROTO_UDP, |
139 | /// Raw sockets ([raw(7)](https://man7.org/linux/man-pages/man7/raw.7.html)) |
140 | Raw = libc::IPPROTO_RAW, |
141 | /// Allows applications and other KEXTs to be notified when certain kernel events occur |
142 | /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html)) |
143 | #[cfg (any(target_os = "ios" , target_os = "macos" ))] |
144 | #[cfg_attr (docsrs, doc(cfg(all())))] |
145 | KextEvent = libc::SYSPROTO_EVENT, |
146 | /// Allows applications to configure and control a KEXT |
147 | /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html)) |
148 | #[cfg (any(target_os = "ios" , target_os = "macos" ))] |
149 | #[cfg_attr (docsrs, doc(cfg(all())))] |
150 | KextControl = libc::SYSPROTO_CONTROL, |
151 | /// Receives routing and link updates and may be used to modify the routing tables (both IPv4 and IPv6), IP addresses, link |
152 | // parameters, neighbor setups, queueing disciplines, traffic classes and packet classifiers |
153 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
154 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
155 | #[cfg_attr (docsrs, doc(cfg(all())))] |
156 | NetlinkRoute = libc::NETLINK_ROUTE, |
157 | /// Reserved for user-mode socket protocols |
158 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
159 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
160 | #[cfg_attr (docsrs, doc(cfg(all())))] |
161 | NetlinkUserSock = libc::NETLINK_USERSOCK, |
162 | /// Query information about sockets of various protocol families from the kernel |
163 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
164 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
165 | #[cfg_attr (docsrs, doc(cfg(all())))] |
166 | NetlinkSockDiag = libc::NETLINK_SOCK_DIAG, |
167 | /// SELinux event notifications. |
168 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
169 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
170 | #[cfg_attr (docsrs, doc(cfg(all())))] |
171 | NetlinkSELinux = libc::NETLINK_SELINUX, |
172 | /// Open-iSCSI |
173 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
174 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
175 | #[cfg_attr (docsrs, doc(cfg(all())))] |
176 | NetlinkISCSI = libc::NETLINK_ISCSI, |
177 | /// Auditing |
178 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
179 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
180 | #[cfg_attr (docsrs, doc(cfg(all())))] |
181 | NetlinkAudit = libc::NETLINK_AUDIT, |
182 | /// Access to FIB lookup from user space |
183 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
184 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
185 | #[cfg_attr (docsrs, doc(cfg(all())))] |
186 | NetlinkFIBLookup = libc::NETLINK_FIB_LOOKUP, |
187 | /// Netfilter subsystem |
188 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
189 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
190 | #[cfg_attr (docsrs, doc(cfg(all())))] |
191 | NetlinkNetFilter = libc::NETLINK_NETFILTER, |
192 | /// SCSI Transports |
193 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
194 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
195 | #[cfg_attr (docsrs, doc(cfg(all())))] |
196 | NetlinkSCSITransport = libc::NETLINK_SCSITRANSPORT, |
197 | /// Infiniband RDMA |
198 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
199 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
200 | #[cfg_attr (docsrs, doc(cfg(all())))] |
201 | NetlinkRDMA = libc::NETLINK_RDMA, |
202 | /// Transport IPv6 packets from netfilter to user space. Used by ip6_queue kernel module. |
203 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
204 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
205 | #[cfg_attr (docsrs, doc(cfg(all())))] |
206 | NetlinkIPv6Firewall = libc::NETLINK_IP6_FW, |
207 | /// DECnet routing messages |
208 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
209 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
210 | #[cfg_attr (docsrs, doc(cfg(all())))] |
211 | NetlinkDECNetRoutingMessage = libc::NETLINK_DNRTMSG, |
212 | /// Kernel messages to user space |
213 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
214 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
215 | #[cfg_attr (docsrs, doc(cfg(all())))] |
216 | NetlinkKObjectUEvent = libc::NETLINK_KOBJECT_UEVENT, |
217 | /// Netlink interface to request information about ciphers registered with the kernel crypto API as well as allow |
218 | /// configuration of the kernel crypto API. |
219 | /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) |
220 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
221 | #[cfg_attr (docsrs, doc(cfg(all())))] |
222 | NetlinkCrypto = libc::NETLINK_CRYPTO, |
223 | /// Non-DIX type protocol number defined for the Ethernet IEEE 802.3 interface that allows packets of all protocols |
224 | /// defined in the interface to be received. |
225 | /// ([ref](https://man7.org/linux/man-pages/man7/packet.7.html)) |
226 | // The protocol number is fed into the socket syscall in network byte order. |
227 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
228 | #[cfg_attr (docsrs, doc(cfg(all())))] |
229 | EthAll = (libc::ETH_P_ALL as u16).to_be() as i32, |
230 | } |
231 | |
232 | #[cfg (target_os = "linux" )] |
233 | libc_bitflags! { |
234 | /// Configuration flags for `SO_TIMESTAMPING` interface |
235 | /// |
236 | /// For use with [`Timestamping`][sockopt::Timestamping]. |
237 | /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html) |
238 | pub struct TimestampingFlag: c_uint { |
239 | /// Report any software timestamps when available. |
240 | SOF_TIMESTAMPING_SOFTWARE; |
241 | /// Report hardware timestamps as generated by SOF_TIMESTAMPING_TX_HARDWARE when available. |
242 | SOF_TIMESTAMPING_RAW_HARDWARE; |
243 | /// Collect transmiting timestamps as reported by hardware |
244 | SOF_TIMESTAMPING_TX_HARDWARE; |
245 | /// Collect transmiting timestamps as reported by software |
246 | SOF_TIMESTAMPING_TX_SOFTWARE; |
247 | /// Collect receiving timestamps as reported by hardware |
248 | SOF_TIMESTAMPING_RX_HARDWARE; |
249 | /// Collect receiving timestamps as reported by software |
250 | SOF_TIMESTAMPING_RX_SOFTWARE; |
251 | } |
252 | } |
253 | |
254 | libc_bitflags! { |
255 | /// Additional socket options |
256 | pub struct SockFlag: c_int { |
257 | /// Set non-blocking mode on the new socket |
258 | #[cfg(any(target_os = "android" , |
259 | target_os = "dragonfly" , |
260 | target_os = "freebsd" , |
261 | target_os = "illumos" , |
262 | target_os = "linux" , |
263 | target_os = "netbsd" , |
264 | target_os = "openbsd" ))] |
265 | #[cfg_attr(docsrs, doc(cfg(all())))] |
266 | SOCK_NONBLOCK; |
267 | /// Set close-on-exec on the new descriptor |
268 | #[cfg(any(target_os = "android" , |
269 | target_os = "dragonfly" , |
270 | target_os = "freebsd" , |
271 | target_os = "illumos" , |
272 | target_os = "linux" , |
273 | target_os = "netbsd" , |
274 | target_os = "openbsd" ))] |
275 | #[cfg_attr(docsrs, doc(cfg(all())))] |
276 | SOCK_CLOEXEC; |
277 | /// Return `EPIPE` instead of raising `SIGPIPE` |
278 | #[cfg(target_os = "netbsd" )] |
279 | #[cfg_attr(docsrs, doc(cfg(all())))] |
280 | SOCK_NOSIGPIPE; |
281 | /// For domains `AF_INET(6)`, only allow `connect(2)`, `sendto(2)`, or `sendmsg(2)` |
282 | /// to the DNS port (typically 53) |
283 | #[cfg(target_os = "openbsd" )] |
284 | #[cfg_attr(docsrs, doc(cfg(all())))] |
285 | SOCK_DNS; |
286 | } |
287 | } |
288 | |
289 | libc_bitflags! { |
290 | /// Flags for send/recv and their relatives |
291 | pub struct MsgFlags: c_int { |
292 | /// Sends or requests out-of-band data on sockets that support this notion |
293 | /// (e.g., of type [`Stream`](enum.SockType.html)); the underlying protocol must also |
294 | /// support out-of-band data. |
295 | #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 |
296 | MSG_OOB; |
297 | /// Peeks at an incoming message. The data is treated as unread and the next |
298 | /// [`recv()`](fn.recv.html) |
299 | /// or similar function shall still return this data. |
300 | #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 |
301 | MSG_PEEK; |
302 | /// Receive operation blocks until the full amount of data can be |
303 | /// returned. The function may return smaller amount of data if a signal |
304 | /// is caught, an error or disconnect occurs. |
305 | #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 |
306 | MSG_WAITALL; |
307 | /// Enables nonblocking operation; if the operation would block, |
308 | /// `EAGAIN` or `EWOULDBLOCK` is returned. This provides similar |
309 | /// behavior to setting the `O_NONBLOCK` flag |
310 | /// (via the [`fcntl`](../../fcntl/fn.fcntl.html) |
311 | /// `F_SETFL` operation), but differs in that `MSG_DONTWAIT` is a per- |
312 | /// call option, whereas `O_NONBLOCK` is a setting on the open file |
313 | /// description (see [open(2)](https://man7.org/linux/man-pages/man2/open.2.html)), |
314 | /// which will affect all threads in |
315 | /// the calling process and as well as other processes that hold |
316 | /// file descriptors referring to the same open file description. |
317 | #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 |
318 | MSG_DONTWAIT; |
319 | /// Receive flags: Control Data was discarded (buffer too small) |
320 | #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 |
321 | MSG_CTRUNC; |
322 | /// For raw ([`Packet`](addr/enum.AddressFamily.html)), Internet datagram |
323 | /// (since Linux 2.4.27/2.6.8), |
324 | /// netlink (since Linux 2.6.22) and UNIX datagram (since Linux 3.4) |
325 | /// sockets: return the real length of the packet or datagram, even |
326 | /// when it was longer than the passed buffer. Not implemented for UNIX |
327 | /// domain ([unix(7)](https://linux.die.net/man/7/unix)) sockets. |
328 | /// |
329 | /// For use with Internet stream sockets, see [tcp(7)](https://linux.die.net/man/7/tcp). |
330 | #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 |
331 | MSG_TRUNC; |
332 | /// Terminates a record (when this notion is supported, as for |
333 | /// sockets of type [`SeqPacket`](enum.SockType.html)). |
334 | #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 |
335 | MSG_EOR; |
336 | /// This flag specifies that queued errors should be received from |
337 | /// the socket error queue. (For more details, see |
338 | /// [recvfrom(2)](https://linux.die.net/man/2/recvfrom)) |
339 | #[cfg(any(target_os = "android" , target_os = "linux" ))] |
340 | #[cfg_attr(docsrs, doc(cfg(all())))] |
341 | #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 |
342 | MSG_ERRQUEUE; |
343 | /// Set the `close-on-exec` flag for the file descriptor received via a UNIX domain |
344 | /// file descriptor using the `SCM_RIGHTS` operation (described in |
345 | /// [unix(7)](https://linux.die.net/man/7/unix)). |
346 | /// This flag is useful for the same reasons as the `O_CLOEXEC` flag of |
347 | /// [open(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html). |
348 | /// |
349 | /// Only used in [`recvmsg`](fn.recvmsg.html) function. |
350 | #[cfg(any(target_os = "android" , |
351 | target_os = "dragonfly" , |
352 | target_os = "freebsd" , |
353 | target_os = "linux" , |
354 | target_os = "netbsd" , |
355 | target_os = "openbsd" ))] |
356 | #[cfg_attr(docsrs, doc(cfg(all())))] |
357 | #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 |
358 | MSG_CMSG_CLOEXEC; |
359 | /// Requests not to send `SIGPIPE` errors when the other end breaks the connection. |
360 | /// (For more details, see [send(2)](https://linux.die.net/man/2/send)). |
361 | #[cfg(any(target_os = "android" , |
362 | target_os = "dragonfly" , |
363 | target_os = "freebsd" , |
364 | target_os = "fuchsia" , |
365 | target_os = "haiku" , |
366 | target_os = "illumos" , |
367 | target_os = "linux" , |
368 | target_os = "netbsd" , |
369 | target_os = "openbsd" , |
370 | target_os = "solaris" ))] |
371 | #[cfg_attr(docsrs, doc(cfg(all())))] |
372 | #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 |
373 | MSG_NOSIGNAL; |
374 | } |
375 | } |
376 | |
377 | cfg_if! { |
378 | if #[cfg(any(target_os = "android" , target_os = "linux" ))] { |
379 | /// Unix credentials of the sending process. |
380 | /// |
381 | /// This struct is used with the `SO_PEERCRED` ancillary message |
382 | /// and the `SCM_CREDENTIALS` control message for UNIX sockets. |
383 | #[repr (transparent)] |
384 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
385 | pub struct UnixCredentials(libc::ucred); |
386 | |
387 | impl UnixCredentials { |
388 | /// Creates a new instance with the credentials of the current process |
389 | pub fn new() -> Self { |
390 | // Safe because these FFI functions are inherently safe |
391 | unsafe { |
392 | UnixCredentials(libc::ucred { |
393 | pid: libc::getpid(), |
394 | uid: libc::getuid(), |
395 | gid: libc::getgid() |
396 | }) |
397 | } |
398 | } |
399 | |
400 | /// Returns the process identifier |
401 | pub fn pid(&self) -> libc::pid_t { |
402 | self.0.pid |
403 | } |
404 | |
405 | /// Returns the user identifier |
406 | pub fn uid(&self) -> libc::uid_t { |
407 | self.0.uid |
408 | } |
409 | |
410 | /// Returns the group identifier |
411 | pub fn gid(&self) -> libc::gid_t { |
412 | self.0.gid |
413 | } |
414 | } |
415 | |
416 | impl Default for UnixCredentials { |
417 | fn default() -> Self { |
418 | Self::new() |
419 | } |
420 | } |
421 | |
422 | impl From<libc::ucred> for UnixCredentials { |
423 | fn from(cred: libc::ucred) -> Self { |
424 | UnixCredentials(cred) |
425 | } |
426 | } |
427 | |
428 | impl From<UnixCredentials> for libc::ucred { |
429 | fn from(uc: UnixCredentials) -> Self { |
430 | uc.0 |
431 | } |
432 | } |
433 | } else if #[cfg(any(target_os = "freebsd" , target_os = "dragonfly" ))] { |
434 | /// Unix credentials of the sending process. |
435 | /// |
436 | /// This struct is used with the `SCM_CREDS` ancillary message for UNIX sockets. |
437 | #[repr (transparent)] |
438 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
439 | pub struct UnixCredentials(libc::cmsgcred); |
440 | |
441 | impl UnixCredentials { |
442 | /// Returns the process identifier |
443 | pub fn pid(&self) -> libc::pid_t { |
444 | self.0.cmcred_pid |
445 | } |
446 | |
447 | /// Returns the real user identifier |
448 | pub fn uid(&self) -> libc::uid_t { |
449 | self.0.cmcred_uid |
450 | } |
451 | |
452 | /// Returns the effective user identifier |
453 | pub fn euid(&self) -> libc::uid_t { |
454 | self.0.cmcred_euid |
455 | } |
456 | |
457 | /// Returns the real group identifier |
458 | pub fn gid(&self) -> libc::gid_t { |
459 | self.0.cmcred_gid |
460 | } |
461 | |
462 | /// Returns a list group identifiers (the first one being the effective GID) |
463 | pub fn groups(&self) -> &[libc::gid_t] { |
464 | unsafe { |
465 | slice::from_raw_parts( |
466 | self.0.cmcred_groups.as_ptr() as *const libc::gid_t, |
467 | self.0.cmcred_ngroups as _ |
468 | ) |
469 | } |
470 | } |
471 | } |
472 | |
473 | impl From<libc::cmsgcred> for UnixCredentials { |
474 | fn from(cred: libc::cmsgcred) -> Self { |
475 | UnixCredentials(cred) |
476 | } |
477 | } |
478 | } |
479 | } |
480 | |
481 | cfg_if! { |
482 | if #[cfg(any( |
483 | target_os = "dragonfly" , |
484 | target_os = "freebsd" , |
485 | target_os = "macos" , |
486 | target_os = "ios" |
487 | ))] { |
488 | /// Return type of [`LocalPeerCred`](crate::sys::socket::sockopt::LocalPeerCred) |
489 | #[repr (transparent)] |
490 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
491 | pub struct XuCred(libc::xucred); |
492 | |
493 | impl XuCred { |
494 | /// Structure layout version |
495 | pub fn version(&self) -> u32 { |
496 | self.0.cr_version |
497 | } |
498 | |
499 | /// Effective user ID |
500 | pub fn uid(&self) -> libc::uid_t { |
501 | self.0.cr_uid |
502 | } |
503 | |
504 | /// Returns a list of group identifiers (the first one being the |
505 | /// effective GID) |
506 | pub fn groups(&self) -> &[libc::gid_t] { |
507 | &self.0.cr_groups |
508 | } |
509 | } |
510 | } |
511 | } |
512 | |
513 | feature! { |
514 | #![feature = "net" ] |
515 | /// Request for multicast socket operations |
516 | /// |
517 | /// This is a wrapper type around `ip_mreq`. |
518 | #[repr (transparent)] |
519 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
520 | pub struct IpMembershipRequest(libc::ip_mreq); |
521 | |
522 | impl IpMembershipRequest { |
523 | /// Instantiate a new `IpMembershipRequest` |
524 | /// |
525 | /// If `interface` is `None`, then `Ipv4Addr::any()` will be used for the interface. |
526 | pub fn new(group: net::Ipv4Addr, interface: Option<net::Ipv4Addr>) |
527 | -> Self |
528 | { |
529 | let imr_addr = match interface { |
530 | None => net::Ipv4Addr::UNSPECIFIED, |
531 | Some(addr) => addr |
532 | }; |
533 | IpMembershipRequest(libc::ip_mreq { |
534 | imr_multiaddr: ipv4addr_to_libc(group), |
535 | imr_interface: ipv4addr_to_libc(imr_addr) |
536 | }) |
537 | } |
538 | } |
539 | |
540 | /// Request for ipv6 multicast socket operations |
541 | /// |
542 | /// This is a wrapper type around `ipv6_mreq`. |
543 | #[repr (transparent)] |
544 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
545 | pub struct Ipv6MembershipRequest(libc::ipv6_mreq); |
546 | |
547 | impl Ipv6MembershipRequest { |
548 | /// Instantiate a new `Ipv6MembershipRequest` |
549 | pub const fn new(group: net::Ipv6Addr) -> Self { |
550 | Ipv6MembershipRequest(libc::ipv6_mreq { |
551 | ipv6mr_multiaddr: ipv6addr_to_libc(&group), |
552 | ipv6mr_interface: 0, |
553 | }) |
554 | } |
555 | } |
556 | } |
557 | |
558 | feature! { |
559 | #![feature = "uio" ] |
560 | |
561 | /// Create a buffer large enough for storing some control messages as returned |
562 | /// by [`recvmsg`](fn.recvmsg.html). |
563 | /// |
564 | /// # Examples |
565 | /// |
566 | /// ``` |
567 | /// # #[macro_use] extern crate nix; |
568 | /// # use nix::sys::time::TimeVal; |
569 | /// # use std::os::unix::io::RawFd; |
570 | /// # fn main() { |
571 | /// // Create a buffer for a `ControlMessageOwned::ScmTimestamp` message |
572 | /// let _ = cmsg_space!(TimeVal); |
573 | /// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message |
574 | /// // with two file descriptors |
575 | /// let _ = cmsg_space!([RawFd; 2]); |
576 | /// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message |
577 | /// // and a `ControlMessageOwned::ScmTimestamp` message |
578 | /// let _ = cmsg_space!(RawFd, TimeVal); |
579 | /// # } |
580 | /// ``` |
581 | // Unfortunately, CMSG_SPACE isn't a const_fn, or else we could return a |
582 | // stack-allocated array. |
583 | #[macro_export ] |
584 | macro_rules! cmsg_space { |
585 | ( $( $x:ty ),* ) => { |
586 | { |
587 | let mut space = 0; |
588 | $( |
589 | // CMSG_SPACE is always safe |
590 | space += unsafe { |
591 | $crate::sys::socket::CMSG_SPACE(::std::mem::size_of::<$x>() as $crate::sys::socket::c_uint) |
592 | } as usize; |
593 | )* |
594 | Vec::<u8>::with_capacity(space) |
595 | } |
596 | } |
597 | } |
598 | |
599 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
600 | /// Contains outcome of sending or receiving a message |
601 | /// |
602 | /// Use [`cmsgs`][RecvMsg::cmsgs] to access all the control messages present, and |
603 | /// [`iovs`][RecvMsg::iovs`] to access underlying io slices. |
604 | pub struct RecvMsg<'a, 's, S> { |
605 | pub bytes: usize, |
606 | cmsghdr: Option<&'a cmsghdr>, |
607 | pub address: Option<S>, |
608 | pub flags: MsgFlags, |
609 | iobufs: std::marker::PhantomData<& 's()>, |
610 | mhdr: msghdr, |
611 | } |
612 | |
613 | impl<'a, S> RecvMsg<'a, '_, S> { |
614 | /// Iterate over the valid control messages pointed to by this |
615 | /// msghdr. |
616 | pub fn cmsgs(&self) -> CmsgIterator { |
617 | CmsgIterator { |
618 | cmsghdr: self.cmsghdr, |
619 | mhdr: &self.mhdr |
620 | } |
621 | } |
622 | } |
623 | |
624 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
625 | pub struct CmsgIterator<'a> { |
626 | /// Control message buffer to decode from. Must adhere to cmsg alignment. |
627 | cmsghdr: Option<&'a cmsghdr>, |
628 | mhdr: &'a msghdr |
629 | } |
630 | |
631 | impl<'a> Iterator for CmsgIterator<'a> { |
632 | type Item = ControlMessageOwned; |
633 | |
634 | fn next(&mut self) -> Option<ControlMessageOwned> { |
635 | match self.cmsghdr { |
636 | None => None, // No more messages |
637 | Some(hdr) => { |
638 | // Get the data. |
639 | // Safe if cmsghdr points to valid data returned by recvmsg(2) |
640 | let cm = unsafe { Some(ControlMessageOwned::decode_from(hdr))}; |
641 | // Advance the internal pointer. Safe if mhdr and cmsghdr point |
642 | // to valid data returned by recvmsg(2) |
643 | self.cmsghdr = unsafe { |
644 | let p = CMSG_NXTHDR(self.mhdr as *const _, hdr as *const _); |
645 | p.as_ref() |
646 | }; |
647 | cm |
648 | } |
649 | } |
650 | } |
651 | } |
652 | |
653 | /// A type-safe wrapper around a single control message, as used with |
654 | /// [`recvmsg`](#fn.recvmsg). |
655 | /// |
656 | /// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html) |
657 | // Nix version 0.13.0 and earlier used ControlMessage for both recvmsg and |
658 | // sendmsg. However, on some platforms the messages returned by recvmsg may be |
659 | // unaligned. ControlMessageOwned takes those messages by copy, obviating any |
660 | // alignment issues. |
661 | // |
662 | // See https://github.com/nix-rust/nix/issues/999 |
663 | #[derive (Clone, Debug, Eq, PartialEq)] |
664 | #[non_exhaustive ] |
665 | pub enum ControlMessageOwned { |
666 | /// Received version of [`ControlMessage::ScmRights`] |
667 | ScmRights(Vec<RawFd>), |
668 | /// Received version of [`ControlMessage::ScmCredentials`] |
669 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
670 | #[cfg_attr (docsrs, doc(cfg(all())))] |
671 | ScmCredentials(UnixCredentials), |
672 | /// Received version of [`ControlMessage::ScmCreds`] |
673 | #[cfg (any(target_os = "freebsd" , target_os = "dragonfly" ))] |
674 | #[cfg_attr (docsrs, doc(cfg(all())))] |
675 | ScmCreds(UnixCredentials), |
676 | /// A message of type `SCM_TIMESTAMP`, containing the time the |
677 | /// packet was received by the kernel. |
678 | /// |
679 | /// See the kernel's explanation in "SO_TIMESTAMP" of |
680 | /// [networking/timestamping](https://www.kernel.org/doc/Documentation/networking/timestamping.txt). |
681 | /// |
682 | /// # Examples |
683 | /// |
684 | /// ``` |
685 | /// # #[macro_use] extern crate nix; |
686 | /// # use nix::sys::socket::*; |
687 | /// # use nix::sys::time::*; |
688 | /// # use std::io::{IoSlice, IoSliceMut}; |
689 | /// # use std::time::*; |
690 | /// # use std::str::FromStr; |
691 | /// # fn main() { |
692 | /// // Set up |
693 | /// let message = "Ohayō!".as_bytes(); |
694 | /// let in_socket = socket( |
695 | /// AddressFamily::Inet, |
696 | /// SockType::Datagram, |
697 | /// SockFlag::empty(), |
698 | /// None).unwrap(); |
699 | /// setsockopt(in_socket, sockopt::ReceiveTimestamp, &true).unwrap(); |
700 | /// let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap(); |
701 | /// bind(in_socket, &localhost).unwrap(); |
702 | /// let address: SockaddrIn = getsockname(in_socket).unwrap(); |
703 | /// // Get initial time |
704 | /// let time0 = SystemTime::now(); |
705 | /// // Send the message |
706 | /// let iov = [IoSlice::new(message)]; |
707 | /// let flags = MsgFlags::empty(); |
708 | /// let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap(); |
709 | /// assert_eq!(message.len(), l); |
710 | /// // Receive the message |
711 | /// let mut buffer = vec![0u8; message.len()]; |
712 | /// let mut cmsgspace = cmsg_space!(TimeVal); |
713 | /// let mut iov = [IoSliceMut::new(&mut buffer)]; |
714 | /// let r = recvmsg::<SockaddrIn>(in_socket, &mut iov, Some(&mut cmsgspace), flags) |
715 | /// .unwrap(); |
716 | /// let rtime = match r.cmsgs().next() { |
717 | /// Some(ControlMessageOwned::ScmTimestamp(rtime)) => rtime, |
718 | /// Some(_) => panic!("Unexpected control message"), |
719 | /// None => panic!("No control message") |
720 | /// }; |
721 | /// // Check the final time |
722 | /// let time1 = SystemTime::now(); |
723 | /// // the packet's received timestamp should lie in-between the two system |
724 | /// // times, unless the system clock was adjusted in the meantime. |
725 | /// let rduration = Duration::new(rtime.tv_sec() as u64, |
726 | /// rtime.tv_usec() as u32 * 1000); |
727 | /// assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration); |
728 | /// assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap()); |
729 | /// // Close socket |
730 | /// nix::unistd::close(in_socket).unwrap(); |
731 | /// # } |
732 | /// ``` |
733 | ScmTimestamp(TimeVal), |
734 | /// A set of nanosecond resolution timestamps |
735 | /// |
736 | /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html) |
737 | #[cfg (all(target_os = "linux" ))] |
738 | ScmTimestampsns(Timestamps), |
739 | /// Nanoseconds resolution timestamp |
740 | /// |
741 | /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html) |
742 | #[cfg (all(target_os = "linux" ))] |
743 | #[cfg_attr (docsrs, doc(cfg(all())))] |
744 | ScmTimestampns(TimeSpec), |
745 | #[cfg (any( |
746 | target_os = "android" , |
747 | target_os = "ios" , |
748 | target_os = "linux" , |
749 | target_os = "macos" , |
750 | target_os = "netbsd" , |
751 | ))] |
752 | #[cfg (feature = "net" )] |
753 | #[cfg_attr (docsrs, doc(cfg(feature = "net" )))] |
754 | Ipv4PacketInfo(libc::in_pktinfo), |
755 | #[cfg (any( |
756 | target_os = "android" , |
757 | target_os = "dragonfly" , |
758 | target_os = "freebsd" , |
759 | target_os = "ios" , |
760 | target_os = "linux" , |
761 | target_os = "macos" , |
762 | target_os = "openbsd" , |
763 | target_os = "netbsd" , |
764 | ))] |
765 | #[cfg (feature = "net" )] |
766 | #[cfg_attr (docsrs, doc(cfg(feature = "net" )))] |
767 | Ipv6PacketInfo(libc::in6_pktinfo), |
768 | #[cfg (any( |
769 | target_os = "freebsd" , |
770 | target_os = "ios" , |
771 | target_os = "macos" , |
772 | target_os = "netbsd" , |
773 | target_os = "openbsd" , |
774 | ))] |
775 | #[cfg (feature = "net" )] |
776 | #[cfg_attr (docsrs, doc(cfg(feature = "net" )))] |
777 | Ipv4RecvIf(libc::sockaddr_dl), |
778 | #[cfg (any( |
779 | target_os = "freebsd" , |
780 | target_os = "ios" , |
781 | target_os = "macos" , |
782 | target_os = "netbsd" , |
783 | target_os = "openbsd" , |
784 | ))] |
785 | #[cfg (feature = "net" )] |
786 | #[cfg_attr (docsrs, doc(cfg(feature = "net" )))] |
787 | Ipv4RecvDstAddr(libc::in_addr), |
788 | #[cfg (any(target_os = "android" , target_os = "freebsd" , target_os = "linux" ))] |
789 | #[cfg (feature = "net" )] |
790 | #[cfg_attr (docsrs, doc(cfg(feature = "net" )))] |
791 | Ipv4OrigDstAddr(libc::sockaddr_in), |
792 | #[cfg (any(target_os = "android" , target_os = "freebsd" , target_os = "linux" ))] |
793 | #[cfg (feature = "net" )] |
794 | #[cfg_attr (docsrs, doc(cfg(feature = "net" )))] |
795 | Ipv6OrigDstAddr(libc::sockaddr_in6), |
796 | |
797 | /// UDP Generic Receive Offload (GRO) allows receiving multiple UDP |
798 | /// packets from a single sender. |
799 | /// Fixed-size payloads are following one by one in a receive buffer. |
800 | /// This Control Message indicates the size of all smaller packets, |
801 | /// except, maybe, the last one. |
802 | /// |
803 | /// `UdpGroSegment` socket option should be enabled on a socket |
804 | /// to allow receiving GRO packets. |
805 | #[cfg (target_os = "linux" )] |
806 | #[cfg (feature = "net" )] |
807 | #[cfg_attr (docsrs, doc(cfg(feature = "net" )))] |
808 | UdpGroSegments(u16), |
809 | |
810 | /// SO_RXQ_OVFL indicates that an unsigned 32 bit value |
811 | /// ancilliary msg (cmsg) should be attached to recieved |
812 | /// skbs indicating the number of packets dropped by the |
813 | /// socket between the last recieved packet and this |
814 | /// received packet. |
815 | /// |
816 | /// `RxqOvfl` socket option should be enabled on a socket |
817 | /// to allow receiving the drop counter. |
818 | #[cfg (any(target_os = "android" , target_os = "fuchsia" , target_os = "linux" ))] |
819 | #[cfg_attr (docsrs, doc(cfg(all())))] |
820 | RxqOvfl(u32), |
821 | |
822 | /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag. |
823 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
824 | #[cfg (feature = "net" )] |
825 | #[cfg_attr (docsrs, doc(cfg(feature = "net" )))] |
826 | Ipv4RecvErr(libc::sock_extended_err, Option<sockaddr_in>), |
827 | /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag. |
828 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
829 | #[cfg (feature = "net" )] |
830 | #[cfg_attr (docsrs, doc(cfg(feature = "net" )))] |
831 | Ipv6RecvErr(libc::sock_extended_err, Option<sockaddr_in6>), |
832 | |
833 | /// Catch-all variant for unimplemented cmsg types. |
834 | #[doc (hidden)] |
835 | Unknown(UnknownCmsg), |
836 | } |
837 | |
838 | /// For representing packet timestamps via `SO_TIMESTAMPING` interface |
839 | #[cfg (all(target_os = "linux" ))] |
840 | #[derive (Copy, Clone, Debug, Eq, PartialEq)] |
841 | pub struct Timestamps { |
842 | /// software based timestamp, usually one containing data |
843 | pub system: TimeSpec, |
844 | /// legacy timestamp, usually empty |
845 | pub hw_trans: TimeSpec, |
846 | /// hardware based timestamp |
847 | pub hw_raw: TimeSpec, |
848 | } |
849 | |
850 | impl ControlMessageOwned { |
851 | /// Decodes a `ControlMessageOwned` from raw bytes. |
852 | /// |
853 | /// This is only safe to call if the data is correct for the message type |
854 | /// specified in the header. Normally, the kernel ensures that this is the |
855 | /// case. "Correct" in this case includes correct length, alignment and |
856 | /// actual content. |
857 | // Clippy complains about the pointer alignment of `p`, not understanding |
858 | // that it's being fed to a function that can handle that. |
859 | #[allow (clippy::cast_ptr_alignment)] |
860 | unsafe fn decode_from(header: &cmsghdr) -> ControlMessageOwned |
861 | { |
862 | let p = CMSG_DATA(header); |
863 | // The cast is not unnecessary on all platforms. |
864 | #[allow (clippy::unnecessary_cast)] |
865 | let len = header as *const _ as usize + header.cmsg_len as usize |
866 | - p as usize; |
867 | match (header.cmsg_level, header.cmsg_type) { |
868 | (libc::SOL_SOCKET, libc::SCM_RIGHTS) => { |
869 | let n = len / mem::size_of::<RawFd>(); |
870 | let mut fds = Vec::with_capacity(n); |
871 | for i in 0..n { |
872 | let fdp = (p as *const RawFd).add(i); |
873 | fds.push(ptr::read_unaligned(fdp)); |
874 | } |
875 | ControlMessageOwned::ScmRights(fds) |
876 | }, |
877 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
878 | (libc::SOL_SOCKET, libc::SCM_CREDENTIALS) => { |
879 | let cred: libc::ucred = ptr::read_unaligned(p as *const _); |
880 | ControlMessageOwned::ScmCredentials(cred.into()) |
881 | } |
882 | #[cfg (any(target_os = "freebsd" , target_os = "dragonfly" ))] |
883 | (libc::SOL_SOCKET, libc::SCM_CREDS) => { |
884 | let cred: libc::cmsgcred = ptr::read_unaligned(p as *const _); |
885 | ControlMessageOwned::ScmCreds(cred.into()) |
886 | } |
887 | #[cfg (not(target_os = "haiku" ))] |
888 | (libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => { |
889 | let tv: libc::timeval = ptr::read_unaligned(p as *const _); |
890 | ControlMessageOwned::ScmTimestamp(TimeVal::from(tv)) |
891 | }, |
892 | #[cfg (all(target_os = "linux" ))] |
893 | (libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => { |
894 | let ts: libc::timespec = ptr::read_unaligned(p as *const _); |
895 | ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts)) |
896 | } |
897 | #[cfg (all(target_os = "linux" ))] |
898 | (libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => { |
899 | let tp = p as *const libc::timespec; |
900 | let ts: libc::timespec = ptr::read_unaligned(tp); |
901 | let system = TimeSpec::from(ts); |
902 | let ts: libc::timespec = ptr::read_unaligned(tp.add(1)); |
903 | let hw_trans = TimeSpec::from(ts); |
904 | let ts: libc::timespec = ptr::read_unaligned(tp.add(2)); |
905 | let hw_raw = TimeSpec::from(ts); |
906 | let timestamping = Timestamps { system, hw_trans, hw_raw }; |
907 | ControlMessageOwned::ScmTimestampsns(timestamping) |
908 | } |
909 | #[cfg (any( |
910 | target_os = "android" , |
911 | target_os = "freebsd" , |
912 | target_os = "ios" , |
913 | target_os = "linux" , |
914 | target_os = "macos" |
915 | ))] |
916 | #[cfg (feature = "net" )] |
917 | (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => { |
918 | let info = ptr::read_unaligned(p as *const libc::in6_pktinfo); |
919 | ControlMessageOwned::Ipv6PacketInfo(info) |
920 | } |
921 | #[cfg (any( |
922 | target_os = "android" , |
923 | target_os = "ios" , |
924 | target_os = "linux" , |
925 | target_os = "macos" , |
926 | target_os = "netbsd" , |
927 | ))] |
928 | #[cfg (feature = "net" )] |
929 | (libc::IPPROTO_IP, libc::IP_PKTINFO) => { |
930 | let info = ptr::read_unaligned(p as *const libc::in_pktinfo); |
931 | ControlMessageOwned::Ipv4PacketInfo(info) |
932 | } |
933 | #[cfg (any( |
934 | target_os = "freebsd" , |
935 | target_os = "ios" , |
936 | target_os = "macos" , |
937 | target_os = "netbsd" , |
938 | target_os = "openbsd" , |
939 | ))] |
940 | #[cfg (feature = "net" )] |
941 | (libc::IPPROTO_IP, libc::IP_RECVIF) => { |
942 | let dl = ptr::read_unaligned(p as *const libc::sockaddr_dl); |
943 | ControlMessageOwned::Ipv4RecvIf(dl) |
944 | }, |
945 | #[cfg (any( |
946 | target_os = "freebsd" , |
947 | target_os = "ios" , |
948 | target_os = "macos" , |
949 | target_os = "netbsd" , |
950 | target_os = "openbsd" , |
951 | ))] |
952 | #[cfg (feature = "net" )] |
953 | (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => { |
954 | let dl = ptr::read_unaligned(p as *const libc::in_addr); |
955 | ControlMessageOwned::Ipv4RecvDstAddr(dl) |
956 | }, |
957 | #[cfg (any(target_os = "android" , target_os = "freebsd" , target_os = "linux" ))] |
958 | #[cfg (feature = "net" )] |
959 | (libc::IPPROTO_IP, libc::IP_ORIGDSTADDR) => { |
960 | let dl = ptr::read_unaligned(p as *const libc::sockaddr_in); |
961 | ControlMessageOwned::Ipv4OrigDstAddr(dl) |
962 | }, |
963 | #[cfg (target_os = "linux" )] |
964 | #[cfg (feature = "net" )] |
965 | (libc::SOL_UDP, libc::UDP_GRO) => { |
966 | let gso_size: u16 = ptr::read_unaligned(p as *const _); |
967 | ControlMessageOwned::UdpGroSegments(gso_size) |
968 | }, |
969 | #[cfg (any(target_os = "android" , target_os = "fuchsia" , target_os = "linux" ))] |
970 | (libc::SOL_SOCKET, libc::SO_RXQ_OVFL) => { |
971 | let drop_counter = ptr::read_unaligned(p as *const u32); |
972 | ControlMessageOwned::RxqOvfl(drop_counter) |
973 | }, |
974 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
975 | #[cfg (feature = "net" )] |
976 | (libc::IPPROTO_IP, libc::IP_RECVERR) => { |
977 | let (err, addr) = Self::recv_err_helper::<sockaddr_in>(p, len); |
978 | ControlMessageOwned::Ipv4RecvErr(err, addr) |
979 | }, |
980 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
981 | #[cfg (feature = "net" )] |
982 | (libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => { |
983 | let (err, addr) = Self::recv_err_helper::<sockaddr_in6>(p, len); |
984 | ControlMessageOwned::Ipv6RecvErr(err, addr) |
985 | }, |
986 | #[cfg (any(target_os = "android" , target_os = "freebsd" , target_os = "linux" ))] |
987 | #[cfg (feature = "net" )] |
988 | (libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR) => { |
989 | let dl = ptr::read_unaligned(p as *const libc::sockaddr_in6); |
990 | ControlMessageOwned::Ipv6OrigDstAddr(dl) |
991 | }, |
992 | (_, _) => { |
993 | let sl = slice::from_raw_parts(p, len); |
994 | let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl)); |
995 | ControlMessageOwned::Unknown(ucmsg) |
996 | } |
997 | } |
998 | } |
999 | |
1000 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1001 | #[cfg (feature = "net" )] |
1002 | #[allow (clippy::cast_ptr_alignment)] // False positive |
1003 | unsafe fn recv_err_helper<T>(p: *mut libc::c_uchar, len: usize) -> (libc::sock_extended_err, Option<T>) { |
1004 | let ee = p as *const libc::sock_extended_err; |
1005 | let err = ptr::read_unaligned(ee); |
1006 | |
1007 | // For errors originating on the network, SO_EE_OFFENDER(ee) points inside the p[..len] |
1008 | // CMSG_DATA buffer. For local errors, there is no address included in the control |
1009 | // message, and SO_EE_OFFENDER(ee) points beyond the end of the buffer. So, we need to |
1010 | // validate that the address object is in-bounds before we attempt to copy it. |
1011 | let addrp = libc::SO_EE_OFFENDER(ee) as *const T; |
1012 | |
1013 | if addrp.offset(1) as usize - (p as usize) > len { |
1014 | (err, None) |
1015 | } else { |
1016 | (err, Some(ptr::read_unaligned(addrp))) |
1017 | } |
1018 | } |
1019 | } |
1020 | |
1021 | /// A type-safe zero-copy wrapper around a single control message, as used wih |
1022 | /// [`sendmsg`](#fn.sendmsg). More types may be added to this enum; do not |
1023 | /// exhaustively pattern-match it. |
1024 | /// |
1025 | /// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html) |
1026 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
1027 | #[non_exhaustive ] |
1028 | pub enum ControlMessage<'a> { |
1029 | /// A message of type `SCM_RIGHTS`, containing an array of file |
1030 | /// descriptors passed between processes. |
1031 | /// |
1032 | /// See the description in the "Ancillary messages" section of the |
1033 | /// [unix(7) man page](https://man7.org/linux/man-pages/man7/unix.7.html). |
1034 | /// |
1035 | /// Using multiple `ScmRights` messages for a single `sendmsg` call isn't |
1036 | /// recommended since it causes platform-dependent behaviour: It might |
1037 | /// swallow all but the first `ScmRights` message or fail with `EINVAL`. |
1038 | /// Instead, you can put all fds to be passed into a single `ScmRights` |
1039 | /// message. |
1040 | ScmRights(&'a [RawFd]), |
1041 | /// A message of type `SCM_CREDENTIALS`, containing the pid, uid and gid of |
1042 | /// a process connected to the socket. |
1043 | /// |
1044 | /// This is similar to the socket option `SO_PEERCRED`, but requires a |
1045 | /// process to explicitly send its credentials. A process running as root is |
1046 | /// allowed to specify any credentials, while credentials sent by other |
1047 | /// processes are verified by the kernel. |
1048 | /// |
1049 | /// For further information, please refer to the |
1050 | /// [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html) man page. |
1051 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1052 | #[cfg_attr (docsrs, doc(cfg(all())))] |
1053 | ScmCredentials(&'a UnixCredentials), |
1054 | /// A message of type `SCM_CREDS`, containing the pid, uid, euid, gid and groups of |
1055 | /// a process connected to the socket. |
1056 | /// |
1057 | /// This is similar to the socket options `LOCAL_CREDS` and `LOCAL_PEERCRED`, but |
1058 | /// requires a process to explicitly send its credentials. |
1059 | /// |
1060 | /// Credentials are always overwritten by the kernel, so this variant does have |
1061 | /// any data, unlike the receive-side |
1062 | /// [`ControlMessageOwned::ScmCreds`]. |
1063 | /// |
1064 | /// For further information, please refer to the |
1065 | /// [`unix(4)`](https://www.freebsd.org/cgi/man.cgi?query=unix) man page. |
1066 | #[cfg (any(target_os = "freebsd" , target_os = "dragonfly" ))] |
1067 | #[cfg_attr (docsrs, doc(cfg(all())))] |
1068 | ScmCreds, |
1069 | |
1070 | /// Set IV for `AF_ALG` crypto API. |
1071 | /// |
1072 | /// For further information, please refer to the |
1073 | /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html) |
1074 | #[cfg (any( |
1075 | target_os = "android" , |
1076 | target_os = "linux" , |
1077 | ))] |
1078 | #[cfg_attr (docsrs, doc(cfg(all())))] |
1079 | AlgSetIv(&'a [u8]), |
1080 | /// Set crypto operation for `AF_ALG` crypto API. It may be one of |
1081 | /// `ALG_OP_ENCRYPT` or `ALG_OP_DECRYPT` |
1082 | /// |
1083 | /// For further information, please refer to the |
1084 | /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html) |
1085 | #[cfg (any( |
1086 | target_os = "android" , |
1087 | target_os = "linux" , |
1088 | ))] |
1089 | #[cfg_attr (docsrs, doc(cfg(all())))] |
1090 | AlgSetOp(&'a libc::c_int), |
1091 | /// Set the length of associated authentication data (AAD) (applicable only to AEAD algorithms) |
1092 | /// for `AF_ALG` crypto API. |
1093 | /// |
1094 | /// For further information, please refer to the |
1095 | /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html) |
1096 | #[cfg (any( |
1097 | target_os = "android" , |
1098 | target_os = "linux" , |
1099 | ))] |
1100 | #[cfg_attr (docsrs, doc(cfg(all())))] |
1101 | AlgSetAeadAssoclen(&'a u32), |
1102 | |
1103 | /// UDP GSO makes it possible for applications to generate network packets |
1104 | /// for a virtual MTU much greater than the real one. |
1105 | /// The length of the send data no longer matches the expected length on |
1106 | /// the wire. |
1107 | /// The size of the datagram payload as it should appear on the wire may be |
1108 | /// passed through this control message. |
1109 | /// Send buffer should consist of multiple fixed-size wire payloads |
1110 | /// following one by one, and the last, possibly smaller one. |
1111 | #[cfg (target_os = "linux" )] |
1112 | #[cfg (feature = "net" )] |
1113 | #[cfg_attr (docsrs, doc(cfg(feature = "net" )))] |
1114 | UdpGsoSegments(&'a u16), |
1115 | |
1116 | /// Configure the sending addressing and interface for v4 |
1117 | /// |
1118 | /// For further information, please refer to the |
1119 | /// [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html) man page. |
1120 | #[cfg (any(target_os = "linux" , |
1121 | target_os = "macos" , |
1122 | target_os = "netbsd" , |
1123 | target_os = "android" , |
1124 | target_os = "ios" ,))] |
1125 | #[cfg (feature = "net" )] |
1126 | #[cfg_attr (docsrs, doc(cfg(feature = "net" )))] |
1127 | Ipv4PacketInfo(&'a libc::in_pktinfo), |
1128 | |
1129 | /// Configure the sending addressing and interface for v6 |
1130 | /// |
1131 | /// For further information, please refer to the |
1132 | /// [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html) man page. |
1133 | #[cfg (any(target_os = "linux" , |
1134 | target_os = "macos" , |
1135 | target_os = "netbsd" , |
1136 | target_os = "freebsd" , |
1137 | target_os = "android" , |
1138 | target_os = "ios" ,))] |
1139 | #[cfg (feature = "net" )] |
1140 | #[cfg_attr (docsrs, doc(cfg(feature = "net" )))] |
1141 | Ipv6PacketInfo(&'a libc::in6_pktinfo), |
1142 | |
1143 | /// Configure the IPv4 source address with `IP_SENDSRCADDR`. |
1144 | #[cfg (any( |
1145 | target_os = "netbsd" , |
1146 | target_os = "freebsd" , |
1147 | target_os = "openbsd" , |
1148 | target_os = "dragonfly" , |
1149 | ))] |
1150 | #[cfg (feature = "net" )] |
1151 | #[cfg_attr (docsrs, doc(cfg(feature = "net" )))] |
1152 | Ipv4SendSrcAddr(&'a libc::in_addr), |
1153 | |
1154 | /// SO_RXQ_OVFL indicates that an unsigned 32 bit value |
1155 | /// ancilliary msg (cmsg) should be attached to recieved |
1156 | /// skbs indicating the number of packets dropped by the |
1157 | /// socket between the last recieved packet and this |
1158 | /// received packet. |
1159 | #[cfg (any(target_os = "android" , target_os = "fuchsia" , target_os = "linux" ))] |
1160 | #[cfg_attr (docsrs, doc(cfg(all())))] |
1161 | RxqOvfl(&'a u32), |
1162 | |
1163 | /// Configure the transmission time of packets. |
1164 | /// |
1165 | /// For further information, please refer to the |
1166 | /// [`tc-etf(8)`](https://man7.org/linux/man-pages/man8/tc-etf.8.html) man |
1167 | /// page. |
1168 | #[cfg (target_os = "linux" )] |
1169 | TxTime(&'a u64), |
1170 | } |
1171 | |
1172 | // An opaque structure used to prevent cmsghdr from being a public type |
1173 | #[doc (hidden)] |
1174 | #[derive (Clone, Debug, Eq, PartialEq)] |
1175 | pub struct UnknownCmsg(cmsghdr, Vec<u8>); |
1176 | |
1177 | impl<'a> ControlMessage<'a> { |
1178 | /// The value of CMSG_SPACE on this message. |
1179 | /// Safe because CMSG_SPACE is always safe |
1180 | fn space(&self) -> usize { |
1181 | unsafe{CMSG_SPACE(self.len() as libc::c_uint) as usize} |
1182 | } |
1183 | |
1184 | /// The value of CMSG_LEN on this message. |
1185 | /// Safe because CMSG_LEN is always safe |
1186 | #[cfg (any(target_os = "android" , |
1187 | all(target_os = "linux" , not(target_env = "musl" ))))] |
1188 | fn cmsg_len(&self) -> usize { |
1189 | unsafe{CMSG_LEN(self.len() as libc::c_uint) as usize} |
1190 | } |
1191 | |
1192 | #[cfg (not(any(target_os = "android" , |
1193 | all(target_os = "linux" , not(target_env = "musl" )))))] |
1194 | fn cmsg_len(&self) -> libc::c_uint { |
1195 | unsafe{CMSG_LEN(self.len() as libc::c_uint)} |
1196 | } |
1197 | |
1198 | /// Return a reference to the payload data as a byte pointer |
1199 | fn copy_to_cmsg_data(&self, cmsg_data: *mut u8) { |
1200 | let data_ptr = match *self { |
1201 | ControlMessage::ScmRights(fds) => { |
1202 | fds as *const _ as *const u8 |
1203 | }, |
1204 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1205 | ControlMessage::ScmCredentials(creds) => { |
1206 | &creds.0 as *const libc::ucred as *const u8 |
1207 | } |
1208 | #[cfg (any(target_os = "freebsd" , target_os = "dragonfly" ))] |
1209 | ControlMessage::ScmCreds => { |
1210 | // The kernel overwrites the data, we just zero it |
1211 | // to make sure it's not uninitialized memory |
1212 | unsafe { ptr::write_bytes(cmsg_data, 0, self.len()) }; |
1213 | return |
1214 | } |
1215 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1216 | ControlMessage::AlgSetIv(iv) => { |
1217 | #[allow (deprecated)] // https://github.com/rust-lang/libc/issues/1501 |
1218 | let af_alg_iv = libc::af_alg_iv { |
1219 | ivlen: iv.len() as u32, |
1220 | iv: [0u8; 0], |
1221 | }; |
1222 | |
1223 | let size = mem::size_of_val(&af_alg_iv); |
1224 | |
1225 | unsafe { |
1226 | ptr::copy_nonoverlapping( |
1227 | &af_alg_iv as *const _ as *const u8, |
1228 | cmsg_data, |
1229 | size, |
1230 | ); |
1231 | ptr::copy_nonoverlapping( |
1232 | iv.as_ptr(), |
1233 | cmsg_data.add(size), |
1234 | iv.len() |
1235 | ); |
1236 | }; |
1237 | |
1238 | return |
1239 | }, |
1240 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1241 | ControlMessage::AlgSetOp(op) => { |
1242 | op as *const _ as *const u8 |
1243 | }, |
1244 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1245 | ControlMessage::AlgSetAeadAssoclen(len) => { |
1246 | len as *const _ as *const u8 |
1247 | }, |
1248 | #[cfg (target_os = "linux" )] |
1249 | #[cfg (feature = "net" )] |
1250 | ControlMessage::UdpGsoSegments(gso_size) => { |
1251 | gso_size as *const _ as *const u8 |
1252 | }, |
1253 | #[cfg (any(target_os = "linux" , target_os = "macos" , |
1254 | target_os = "netbsd" , target_os = "android" , |
1255 | target_os = "ios" ,))] |
1256 | #[cfg (feature = "net" )] |
1257 | ControlMessage::Ipv4PacketInfo(info) => info as *const _ as *const u8, |
1258 | #[cfg (any(target_os = "linux" , target_os = "macos" , |
1259 | target_os = "netbsd" , target_os = "freebsd" , |
1260 | target_os = "android" , target_os = "ios" ,))] |
1261 | #[cfg (feature = "net" )] |
1262 | ControlMessage::Ipv6PacketInfo(info) => info as *const _ as *const u8, |
1263 | #[cfg (any(target_os = "netbsd" , target_os = "freebsd" , |
1264 | target_os = "openbsd" , target_os = "dragonfly" ))] |
1265 | #[cfg (feature = "net" )] |
1266 | ControlMessage::Ipv4SendSrcAddr(addr) => addr as *const _ as *const u8, |
1267 | #[cfg (any(target_os = "android" , target_os = "fuchsia" , target_os = "linux" ))] |
1268 | ControlMessage::RxqOvfl(drop_count) => { |
1269 | drop_count as *const _ as *const u8 |
1270 | }, |
1271 | #[cfg (target_os = "linux" )] |
1272 | ControlMessage::TxTime(tx_time) => { |
1273 | tx_time as *const _ as *const u8 |
1274 | }, |
1275 | }; |
1276 | unsafe { |
1277 | ptr::copy_nonoverlapping( |
1278 | data_ptr, |
1279 | cmsg_data, |
1280 | self.len() |
1281 | ) |
1282 | }; |
1283 | } |
1284 | |
1285 | /// The size of the payload, excluding its cmsghdr |
1286 | fn len(&self) -> usize { |
1287 | match *self { |
1288 | ControlMessage::ScmRights(fds) => { |
1289 | mem::size_of_val(fds) |
1290 | }, |
1291 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1292 | ControlMessage::ScmCredentials(creds) => { |
1293 | mem::size_of_val(creds) |
1294 | } |
1295 | #[cfg (any(target_os = "freebsd" , target_os = "dragonfly" ))] |
1296 | ControlMessage::ScmCreds => { |
1297 | mem::size_of::<libc::cmsgcred>() |
1298 | } |
1299 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1300 | ControlMessage::AlgSetIv(iv) => { |
1301 | mem::size_of::<&[u8]>() + iv.len() |
1302 | }, |
1303 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1304 | ControlMessage::AlgSetOp(op) => { |
1305 | mem::size_of_val(op) |
1306 | }, |
1307 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1308 | ControlMessage::AlgSetAeadAssoclen(len) => { |
1309 | mem::size_of_val(len) |
1310 | }, |
1311 | #[cfg (target_os = "linux" )] |
1312 | #[cfg (feature = "net" )] |
1313 | ControlMessage::UdpGsoSegments(gso_size) => { |
1314 | mem::size_of_val(gso_size) |
1315 | }, |
1316 | #[cfg (any(target_os = "linux" , target_os = "macos" , |
1317 | target_os = "netbsd" , target_os = "android" , |
1318 | target_os = "ios" ,))] |
1319 | #[cfg (feature = "net" )] |
1320 | ControlMessage::Ipv4PacketInfo(info) => mem::size_of_val(info), |
1321 | #[cfg (any(target_os = "linux" , target_os = "macos" , |
1322 | target_os = "netbsd" , target_os = "freebsd" , |
1323 | target_os = "android" , target_os = "ios" ,))] |
1324 | #[cfg (feature = "net" )] |
1325 | ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info), |
1326 | #[cfg (any(target_os = "netbsd" , target_os = "freebsd" , |
1327 | target_os = "openbsd" , target_os = "dragonfly" ))] |
1328 | #[cfg (feature = "net" )] |
1329 | ControlMessage::Ipv4SendSrcAddr(addr) => mem::size_of_val(addr), |
1330 | #[cfg (any(target_os = "android" , target_os = "fuchsia" , target_os = "linux" ))] |
1331 | ControlMessage::RxqOvfl(drop_count) => { |
1332 | mem::size_of_val(drop_count) |
1333 | }, |
1334 | #[cfg (target_os = "linux" )] |
1335 | ControlMessage::TxTime(tx_time) => { |
1336 | mem::size_of_val(tx_time) |
1337 | }, |
1338 | } |
1339 | } |
1340 | |
1341 | /// Returns the value to put into the `cmsg_level` field of the header. |
1342 | fn cmsg_level(&self) -> libc::c_int { |
1343 | match *self { |
1344 | ControlMessage::ScmRights(_) => libc::SOL_SOCKET, |
1345 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1346 | ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET, |
1347 | #[cfg (any(target_os = "freebsd" , target_os = "dragonfly" ))] |
1348 | ControlMessage::ScmCreds => libc::SOL_SOCKET, |
1349 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1350 | ControlMessage::AlgSetIv(_) | ControlMessage::AlgSetOp(_) | |
1351 | ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG, |
1352 | #[cfg (target_os = "linux" )] |
1353 | #[cfg (feature = "net" )] |
1354 | ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP, |
1355 | #[cfg (any(target_os = "linux" , target_os = "macos" , |
1356 | target_os = "netbsd" , target_os = "android" , |
1357 | target_os = "ios" ,))] |
1358 | #[cfg (feature = "net" )] |
1359 | ControlMessage::Ipv4PacketInfo(_) => libc::IPPROTO_IP, |
1360 | #[cfg (any(target_os = "linux" , target_os = "macos" , |
1361 | target_os = "netbsd" , target_os = "freebsd" , |
1362 | target_os = "android" , target_os = "ios" ,))] |
1363 | #[cfg (feature = "net" )] |
1364 | ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6, |
1365 | #[cfg (any(target_os = "netbsd" , target_os = "freebsd" , |
1366 | target_os = "openbsd" , target_os = "dragonfly" ))] |
1367 | #[cfg (feature = "net" )] |
1368 | ControlMessage::Ipv4SendSrcAddr(_) => libc::IPPROTO_IP, |
1369 | #[cfg (any(target_os = "android" , target_os = "fuchsia" , target_os = "linux" ))] |
1370 | ControlMessage::RxqOvfl(_) => libc::SOL_SOCKET, |
1371 | #[cfg (target_os = "linux" )] |
1372 | ControlMessage::TxTime(_) => libc::SOL_SOCKET, |
1373 | } |
1374 | } |
1375 | |
1376 | /// Returns the value to put into the `cmsg_type` field of the header. |
1377 | fn cmsg_type(&self) -> libc::c_int { |
1378 | match *self { |
1379 | ControlMessage::ScmRights(_) => libc::SCM_RIGHTS, |
1380 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1381 | ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS, |
1382 | #[cfg (any(target_os = "freebsd" , target_os = "dragonfly" ))] |
1383 | ControlMessage::ScmCreds => libc::SCM_CREDS, |
1384 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1385 | ControlMessage::AlgSetIv(_) => { |
1386 | libc::ALG_SET_IV |
1387 | }, |
1388 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1389 | ControlMessage::AlgSetOp(_) => { |
1390 | libc::ALG_SET_OP |
1391 | }, |
1392 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
1393 | ControlMessage::AlgSetAeadAssoclen(_) => { |
1394 | libc::ALG_SET_AEAD_ASSOCLEN |
1395 | }, |
1396 | #[cfg (target_os = "linux" )] |
1397 | #[cfg (feature = "net" )] |
1398 | ControlMessage::UdpGsoSegments(_) => { |
1399 | libc::UDP_SEGMENT |
1400 | }, |
1401 | #[cfg (any(target_os = "linux" , target_os = "macos" , |
1402 | target_os = "netbsd" , target_os = "android" , |
1403 | target_os = "ios" ,))] |
1404 | #[cfg (feature = "net" )] |
1405 | ControlMessage::Ipv4PacketInfo(_) => libc::IP_PKTINFO, |
1406 | #[cfg (any(target_os = "linux" , target_os = "macos" , |
1407 | target_os = "netbsd" , target_os = "freebsd" , |
1408 | target_os = "android" , target_os = "ios" ,))] |
1409 | #[cfg (feature = "net" )] |
1410 | ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO, |
1411 | #[cfg (any(target_os = "netbsd" , target_os = "freebsd" , |
1412 | target_os = "openbsd" , target_os = "dragonfly" ))] |
1413 | #[cfg (feature = "net" )] |
1414 | ControlMessage::Ipv4SendSrcAddr(_) => libc::IP_SENDSRCADDR, |
1415 | #[cfg (any(target_os = "android" , target_os = "fuchsia" , target_os = "linux" ))] |
1416 | ControlMessage::RxqOvfl(_) => { |
1417 | libc::SO_RXQ_OVFL |
1418 | }, |
1419 | #[cfg (target_os = "linux" )] |
1420 | ControlMessage::TxTime(_) => { |
1421 | libc::SCM_TXTIME |
1422 | }, |
1423 | } |
1424 | } |
1425 | |
1426 | // Unsafe: cmsg must point to a valid cmsghdr with enough space to |
1427 | // encode self. |
1428 | unsafe fn encode_into(&self, cmsg: *mut cmsghdr) { |
1429 | (*cmsg).cmsg_level = self.cmsg_level(); |
1430 | (*cmsg).cmsg_type = self.cmsg_type(); |
1431 | (*cmsg).cmsg_len = self.cmsg_len(); |
1432 | self.copy_to_cmsg_data(CMSG_DATA(cmsg)); |
1433 | } |
1434 | } |
1435 | |
1436 | |
1437 | /// Send data in scatter-gather vectors to a socket, possibly accompanied |
1438 | /// by ancillary data. Optionally direct the message at the given address, |
1439 | /// as with sendto. |
1440 | /// |
1441 | /// Allocates if cmsgs is nonempty. |
1442 | /// |
1443 | /// # Examples |
1444 | /// When not directing to any specific address, use `()` for the generic type |
1445 | /// ``` |
1446 | /// # use nix::sys::socket::*; |
1447 | /// # use nix::unistd::pipe; |
1448 | /// # use std::io::IoSlice; |
1449 | /// let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, |
1450 | /// SockFlag::empty()) |
1451 | /// .unwrap(); |
1452 | /// let (r, w) = pipe().unwrap(); |
1453 | /// |
1454 | /// let iov = [IoSlice::new(b"hello")]; |
1455 | /// let fds = [r]; |
1456 | /// let cmsg = ControlMessage::ScmRights(&fds); |
1457 | /// sendmsg::<()>(fd1, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(); |
1458 | /// ``` |
1459 | /// When directing to a specific address, the generic type will be inferred. |
1460 | /// ``` |
1461 | /// # use nix::sys::socket::*; |
1462 | /// # use nix::unistd::pipe; |
1463 | /// # use std::io::IoSlice; |
1464 | /// # use std::str::FromStr; |
1465 | /// let localhost = SockaddrIn::from_str("1.2.3.4:8080").unwrap(); |
1466 | /// let fd = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), |
1467 | /// None).unwrap(); |
1468 | /// let (r, w) = pipe().unwrap(); |
1469 | /// |
1470 | /// let iov = [IoSlice::new(b"hello")]; |
1471 | /// let fds = [r]; |
1472 | /// let cmsg = ControlMessage::ScmRights(&fds); |
1473 | /// sendmsg(fd, &iov, &[cmsg], MsgFlags::empty(), Some(&localhost)).unwrap(); |
1474 | /// ``` |
1475 | pub fn sendmsg<S>(fd: RawFd, iov: &[IoSlice<'_>], cmsgs: &[ControlMessage], |
1476 | flags: MsgFlags, addr: Option<&S>) -> Result<usize> |
1477 | where S: SockaddrLike |
1478 | { |
1479 | let capacity = cmsgs.iter().map(|c| c.space()).sum(); |
1480 | |
1481 | // First size the buffer needed to hold the cmsgs. It must be zeroed, |
1482 | // because subsequent code will not clear the padding bytes. |
1483 | let mut cmsg_buffer = vec![0u8; capacity]; |
1484 | |
1485 | let mhdr = pack_mhdr_to_send(&mut cmsg_buffer[..], iov, cmsgs, addr); |
1486 | |
1487 | let ret = unsafe { libc::sendmsg(fd, &mhdr, flags.bits()) }; |
1488 | |
1489 | Errno::result(ret).map(|r| r as usize) |
1490 | } |
1491 | |
1492 | |
1493 | /// An extension of `sendmsg` that allows the caller to transmit multiple |
1494 | /// messages on a socket using a single system call. This has performance |
1495 | /// benefits for some applications. |
1496 | /// |
1497 | /// Allocations are performed for cmsgs and to build `msghdr` buffer |
1498 | /// |
1499 | /// # Arguments |
1500 | /// |
1501 | /// * `fd`: Socket file descriptor |
1502 | /// * `data`: Struct that implements `IntoIterator` with `SendMmsgData` items |
1503 | /// * `flags`: Optional flags passed directly to the operating system. |
1504 | /// |
1505 | /// # Returns |
1506 | /// `Vec` with numbers of sent bytes on each sent message. |
1507 | /// |
1508 | /// # References |
1509 | /// [`sendmsg`](fn.sendmsg.html) |
1510 | #[cfg (any( |
1511 | target_os = "linux" , |
1512 | target_os = "android" , |
1513 | target_os = "freebsd" , |
1514 | target_os = "netbsd" , |
1515 | ))] |
1516 | pub fn sendmmsg<'a, XS, AS, C, I, S>( |
1517 | fd: RawFd, |
1518 | data: &'a mut MultiHeaders<S>, |
1519 | slices: XS, |
1520 | // one address per group of slices |
1521 | addrs: AS, |
1522 | // shared across all the messages |
1523 | cmsgs: C, |
1524 | flags: MsgFlags |
1525 | ) -> crate::Result<MultiResults<'a, S>> |
1526 | where |
1527 | XS: IntoIterator<Item = &'a I>, |
1528 | AS: AsRef<[Option<S>]>, |
1529 | I: AsRef<[IoSlice<'a>]> + 'a, |
1530 | C: AsRef<[ControlMessage<'a>]> + 'a, |
1531 | S: SockaddrLike + 'a |
1532 | { |
1533 | |
1534 | let mut count = 0; |
1535 | |
1536 | |
1537 | for (i, ((slice, addr), mmsghdr)) in slices.into_iter().zip(addrs.as_ref()).zip(data.items.iter_mut() ).enumerate() { |
1538 | let p = &mut mmsghdr.msg_hdr; |
1539 | p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec; |
1540 | p.msg_iovlen = slice.as_ref().len() as _; |
1541 | |
1542 | p.msg_namelen = addr.as_ref().map_or(0, S::len); |
1543 | p.msg_name = addr.as_ref().map_or(ptr::null(), S::as_ptr) as _; |
1544 | |
1545 | // Encode each cmsg. This must happen after initializing the header because |
1546 | // CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields. |
1547 | // CMSG_FIRSTHDR is always safe |
1548 | let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(p) }; |
1549 | for cmsg in cmsgs.as_ref() { |
1550 | assert_ne!(pmhdr, ptr::null_mut()); |
1551 | // Safe because we know that pmhdr is valid, and we initialized it with |
1552 | // sufficient space |
1553 | unsafe { cmsg.encode_into(pmhdr) }; |
1554 | // Safe because mhdr is valid |
1555 | pmhdr = unsafe { CMSG_NXTHDR(p, pmhdr) }; |
1556 | } |
1557 | |
1558 | count = i+1; |
1559 | } |
1560 | |
1561 | let sent = Errno::result(unsafe { |
1562 | libc::sendmmsg( |
1563 | fd, |
1564 | data.items.as_mut_ptr(), |
1565 | count as _, |
1566 | flags.bits() as _ |
1567 | ) |
1568 | })? as usize; |
1569 | |
1570 | Ok(MultiResults { |
1571 | rmm: data, |
1572 | current_index: 0, |
1573 | received: sent |
1574 | }) |
1575 | |
1576 | } |
1577 | |
1578 | |
1579 | #[cfg (any( |
1580 | target_os = "linux" , |
1581 | target_os = "android" , |
1582 | target_os = "freebsd" , |
1583 | target_os = "netbsd" , |
1584 | ))] |
1585 | #[derive (Debug)] |
1586 | /// Preallocated structures needed for [`recvmmsg`] and [`sendmmsg`] functions |
1587 | pub struct MultiHeaders<S> { |
1588 | // preallocated boxed slice of mmsghdr |
1589 | items: Box<[libc::mmsghdr]>, |
1590 | addresses: Box<[mem::MaybeUninit<S>]>, |
1591 | // while we are not using it directly - this is used to store control messages |
1592 | // and we retain pointers to them inside items array |
1593 | #[allow (dead_code)] |
1594 | cmsg_buffers: Option<Box<[u8]>>, |
1595 | msg_controllen: usize, |
1596 | } |
1597 | |
1598 | #[cfg (any( |
1599 | target_os = "linux" , |
1600 | target_os = "android" , |
1601 | target_os = "freebsd" , |
1602 | target_os = "netbsd" , |
1603 | ))] |
1604 | impl<S> MultiHeaders<S> { |
1605 | /// Preallocate structure used by [`recvmmsg`] and [`sendmmsg`] takes number of headers to preallocate |
1606 | /// |
1607 | /// `cmsg_buffer` should be created with [`cmsg_space!`] if needed |
1608 | pub fn preallocate(num_slices: usize, cmsg_buffer: Option<Vec<u8>>) -> Self |
1609 | where |
1610 | S: Copy + SockaddrLike, |
1611 | { |
1612 | // we will be storing pointers to addresses inside mhdr - convert it into boxed |
1613 | // slice so it can'be changed later by pushing anything into self.addresses |
1614 | let mut addresses = vec![std::mem::MaybeUninit::<S>::uninit(); num_slices].into_boxed_slice(); |
1615 | |
1616 | let msg_controllen = cmsg_buffer.as_ref().map_or(0, |v| v.capacity()); |
1617 | |
1618 | // we'll need a cmsg_buffer for each slice, we preallocate a vector and split |
1619 | // it into "slices" parts |
1620 | let mut cmsg_buffers = |
1621 | cmsg_buffer.map(|v| vec![0u8; v.capacity() * num_slices].into_boxed_slice()); |
1622 | |
1623 | let items = addresses |
1624 | .iter_mut() |
1625 | .enumerate() |
1626 | .map(|(ix, address)| { |
1627 | let (ptr, cap) = match &mut cmsg_buffers { |
1628 | Some(v) => ((&mut v[ix * msg_controllen] as *mut u8), msg_controllen), |
1629 | None => (std::ptr::null_mut(), 0), |
1630 | }; |
1631 | let msg_hdr = unsafe { pack_mhdr_to_receive(std::ptr::null_mut(), 0, ptr, cap, address.as_mut_ptr()) }; |
1632 | libc::mmsghdr { |
1633 | msg_hdr, |
1634 | msg_len: 0, |
1635 | } |
1636 | }) |
1637 | .collect::<Vec<_>>(); |
1638 | |
1639 | Self { |
1640 | items: items.into_boxed_slice(), |
1641 | addresses, |
1642 | cmsg_buffers, |
1643 | msg_controllen, |
1644 | } |
1645 | } |
1646 | } |
1647 | |
1648 | /// An extension of recvmsg that allows the caller to receive multiple messages from a socket using a single system call. |
1649 | /// |
1650 | /// This has performance benefits for some applications. |
1651 | /// |
1652 | /// This method performs no allocations. |
1653 | /// |
1654 | /// Returns an iterator producing [`RecvMsg`], one per received messages. Each `RecvMsg` can produce |
1655 | /// iterators over [`IoSlice`] with [`iovs`][RecvMsg::iovs`] and |
1656 | /// `ControlMessageOwned` with [`cmsgs`][RecvMsg::cmsgs]. |
1657 | /// |
1658 | /// # Bugs (in underlying implementation, at least in Linux) |
1659 | /// The timeout argument does not work as intended. The timeout is checked only after the receipt |
1660 | /// of each datagram, so that if up to `vlen`-1 datagrams are received before the timeout expires, |
1661 | /// but then no further datagrams are received, the call will block forever. |
1662 | /// |
1663 | /// If an error occurs after at least one message has been received, the call succeeds, and returns |
1664 | /// the number of messages received. The error code is expected to be returned on a subsequent |
1665 | /// call to recvmmsg(). In the current implementation, however, the error code can be |
1666 | /// overwritten in the meantime by an unrelated network event on a socket, for example an |
1667 | /// incoming ICMP packet. |
1668 | |
1669 | // On aarch64 linux using recvmmsg and trying to get hardware/kernel timestamps might not |
1670 | // always produce the desired results - see https://github.com/nix-rust/nix/pull/1744 for more |
1671 | // details |
1672 | |
1673 | #[cfg (any( |
1674 | target_os = "linux" , |
1675 | target_os = "android" , |
1676 | target_os = "freebsd" , |
1677 | target_os = "netbsd" , |
1678 | ))] |
1679 | pub fn recvmmsg<'a, XS, S, I>( |
1680 | fd: RawFd, |
1681 | data: &'a mut MultiHeaders<S>, |
1682 | slices: XS, |
1683 | flags: MsgFlags, |
1684 | mut timeout: Option<crate::sys::time::TimeSpec>, |
1685 | ) -> crate::Result<MultiResults<'a, S>> |
1686 | where |
1687 | XS: IntoIterator<Item = &'a I>, |
1688 | I: AsRef<[IoSliceMut<'a>]> + 'a, |
1689 | { |
1690 | let mut count = 0; |
1691 | for (i, (slice, mmsghdr)) in slices.into_iter().zip(data.items.iter_mut()).enumerate() { |
1692 | let p = &mut mmsghdr.msg_hdr; |
1693 | p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec; |
1694 | p.msg_iovlen = slice.as_ref().len() as _; |
1695 | count = i + 1; |
1696 | } |
1697 | |
1698 | let timeout_ptr = timeout |
1699 | .as_mut() |
1700 | .map_or_else(std::ptr::null_mut, |t| t as *mut _ as *mut libc::timespec); |
1701 | |
1702 | let received = Errno::result(unsafe { |
1703 | libc::recvmmsg( |
1704 | fd, |
1705 | data.items.as_mut_ptr(), |
1706 | count as _, |
1707 | flags.bits() as _, |
1708 | timeout_ptr, |
1709 | ) |
1710 | })? as usize; |
1711 | |
1712 | Ok(MultiResults { |
1713 | rmm: data, |
1714 | current_index: 0, |
1715 | received, |
1716 | }) |
1717 | } |
1718 | |
1719 | #[cfg (any( |
1720 | target_os = "linux" , |
1721 | target_os = "android" , |
1722 | target_os = "freebsd" , |
1723 | target_os = "netbsd" , |
1724 | ))] |
1725 | #[derive (Debug)] |
1726 | /// Iterator over results of [`recvmmsg`]/[`sendmmsg`] |
1727 | /// |
1728 | /// |
1729 | pub struct MultiResults<'a, S> { |
1730 | // preallocated structures |
1731 | rmm: &'a MultiHeaders<S>, |
1732 | current_index: usize, |
1733 | received: usize, |
1734 | } |
1735 | |
1736 | #[cfg (any( |
1737 | target_os = "linux" , |
1738 | target_os = "android" , |
1739 | target_os = "freebsd" , |
1740 | target_os = "netbsd" , |
1741 | ))] |
1742 | impl<'a, S> Iterator for MultiResults<'a, S> |
1743 | where |
1744 | S: Copy + SockaddrLike, |
1745 | { |
1746 | type Item = RecvMsg<'a, 'a, S>; |
1747 | |
1748 | // The cast is not unnecessary on all platforms. |
1749 | #[allow (clippy::unnecessary_cast)] |
1750 | fn next(&mut self) -> Option<Self::Item> { |
1751 | if self.current_index >= self.received { |
1752 | return None; |
1753 | } |
1754 | let mmsghdr = self.rmm.items[self.current_index]; |
1755 | |
1756 | // as long as we are not reading past the index writen by recvmmsg - address |
1757 | // will be initialized |
1758 | let address = unsafe { self.rmm.addresses[self.current_index].assume_init() }; |
1759 | |
1760 | self.current_index += 1; |
1761 | Some(unsafe { |
1762 | read_mhdr( |
1763 | mmsghdr.msg_hdr, |
1764 | mmsghdr.msg_len as isize, |
1765 | self.rmm.msg_controllen, |
1766 | address, |
1767 | ) |
1768 | }) |
1769 | } |
1770 | } |
1771 | |
1772 | impl<'a, S> RecvMsg<'_, 'a, S> { |
1773 | /// Iterate over the filled io slices pointed by this msghdr |
1774 | pub fn iovs(&self) -> IoSliceIterator<'a> { |
1775 | IoSliceIterator { |
1776 | index: 0, |
1777 | remaining: self.bytes, |
1778 | slices: unsafe { |
1779 | // safe for as long as mgdr is properly initialized and references are valid. |
1780 | // for multi messages API we initialize it with an empty |
1781 | // slice and replace with a concrete buffer |
1782 | // for single message API we hold a lifetime reference to ioslices |
1783 | std::slice::from_raw_parts(self.mhdr.msg_iov as *const _, self.mhdr.msg_iovlen as _) |
1784 | }, |
1785 | } |
1786 | } |
1787 | } |
1788 | |
1789 | #[derive (Debug)] |
1790 | pub struct IoSliceIterator<'a> { |
1791 | index: usize, |
1792 | remaining: usize, |
1793 | slices: &'a [IoSlice<'a>], |
1794 | } |
1795 | |
1796 | impl<'a> Iterator for IoSliceIterator<'a> { |
1797 | type Item = &'a [u8]; |
1798 | |
1799 | fn next(&mut self) -> Option<Self::Item> { |
1800 | if self.index >= self.slices.len() { |
1801 | return None; |
1802 | } |
1803 | let slice = &self.slices[self.index][..self.remaining.min(self.slices[self.index].len())]; |
1804 | self.remaining -= slice.len(); |
1805 | self.index += 1; |
1806 | if slice.is_empty() { |
1807 | return None; |
1808 | } |
1809 | |
1810 | Some(slice) |
1811 | } |
1812 | } |
1813 | |
1814 | // test contains both recvmmsg and timestaping which is linux only |
1815 | // there are existing tests for recvmmsg only in tests/ |
1816 | #[cfg (target_os = "linux" )] |
1817 | #[cfg (test)] |
1818 | mod test { |
1819 | use crate::sys::socket::{AddressFamily, ControlMessageOwned}; |
1820 | use crate::*; |
1821 | use std::str::FromStr; |
1822 | |
1823 | #[cfg_attr (qemu, ignore)] |
1824 | #[test ] |
1825 | fn test_recvmm2() -> crate::Result<()> { |
1826 | use crate::sys::socket::{ |
1827 | sendmsg, setsockopt, socket, sockopt::Timestamping, MsgFlags, SockFlag, SockType, |
1828 | SockaddrIn, TimestampingFlag, |
1829 | }; |
1830 | use std::io::{IoSlice, IoSliceMut}; |
1831 | |
1832 | let sock_addr = SockaddrIn::from_str("127.0.0.1:6790" ).unwrap(); |
1833 | |
1834 | let ssock = socket( |
1835 | AddressFamily::Inet, |
1836 | SockType::Datagram, |
1837 | SockFlag::empty(), |
1838 | None, |
1839 | )?; |
1840 | |
1841 | let rsock = socket( |
1842 | AddressFamily::Inet, |
1843 | SockType::Datagram, |
1844 | SockFlag::SOCK_NONBLOCK, |
1845 | None, |
1846 | )?; |
1847 | |
1848 | crate::sys::socket::bind(rsock, &sock_addr)?; |
1849 | |
1850 | setsockopt(rsock, Timestamping, &TimestampingFlag::all())?; |
1851 | |
1852 | let sbuf = (0..400).map(|i| i as u8).collect::<Vec<_>>(); |
1853 | |
1854 | let mut recv_buf = vec![0; 1024]; |
1855 | |
1856 | let mut recv_iovs = Vec::new(); |
1857 | let mut pkt_iovs = Vec::new(); |
1858 | |
1859 | for (ix, chunk) in recv_buf.chunks_mut(256).enumerate() { |
1860 | pkt_iovs.push(IoSliceMut::new(chunk)); |
1861 | if ix % 2 == 1 { |
1862 | recv_iovs.push(pkt_iovs); |
1863 | pkt_iovs = Vec::new(); |
1864 | } |
1865 | } |
1866 | drop(pkt_iovs); |
1867 | |
1868 | let flags = MsgFlags::empty(); |
1869 | let iov1 = [IoSlice::new(&sbuf)]; |
1870 | |
1871 | let cmsg = cmsg_space!(crate::sys::socket::Timestamps); |
1872 | sendmsg(ssock, &iov1, &[], flags, Some(&sock_addr)).unwrap(); |
1873 | |
1874 | let mut data = super::MultiHeaders::<()>::preallocate(recv_iovs.len(), Some(cmsg)); |
1875 | |
1876 | let t = sys::time::TimeSpec::from_duration(std::time::Duration::from_secs(10)); |
1877 | |
1878 | let recv = super::recvmmsg(rsock, &mut data, recv_iovs.iter(), flags, Some(t))?; |
1879 | |
1880 | for rmsg in recv { |
1881 | #[cfg (not(any(qemu, target_arch = "aarch64" )))] |
1882 | let mut saw_time = false; |
1883 | let mut recvd = 0; |
1884 | for cmsg in rmsg.cmsgs() { |
1885 | if let ControlMessageOwned::ScmTimestampsns(timestamps) = cmsg { |
1886 | let ts = timestamps.system; |
1887 | |
1888 | let sys_time = |
1889 | crate::time::clock_gettime(crate::time::ClockId::CLOCK_REALTIME)?; |
1890 | let diff = if ts > sys_time { |
1891 | ts - sys_time |
1892 | } else { |
1893 | sys_time - ts |
1894 | }; |
1895 | assert!(std::time::Duration::from(diff).as_secs() < 60); |
1896 | #[cfg (not(any(qemu, target_arch = "aarch64" )))] |
1897 | { |
1898 | saw_time = true; |
1899 | } |
1900 | } |
1901 | } |
1902 | |
1903 | #[cfg (not(any(qemu, target_arch = "aarch64" )))] |
1904 | assert!(saw_time); |
1905 | |
1906 | for iov in rmsg.iovs() { |
1907 | recvd += iov.len(); |
1908 | } |
1909 | assert_eq!(recvd, 400); |
1910 | } |
1911 | |
1912 | Ok(()) |
1913 | } |
1914 | } |
1915 | unsafe fn read_mhdr<'a, 'i, S>( |
1916 | mhdr: msghdr, |
1917 | r: isize, |
1918 | msg_controllen: usize, |
1919 | mut address: S, |
1920 | ) -> RecvMsg<'a, 'i, S> |
1921 | where S: SockaddrLike |
1922 | { |
1923 | // The cast is not unnecessary on all platforms. |
1924 | #[allow (clippy::unnecessary_cast)] |
1925 | let cmsghdr = { |
1926 | if mhdr.msg_controllen > 0 { |
1927 | debug_assert!(!mhdr.msg_control.is_null()); |
1928 | debug_assert!(msg_controllen >= mhdr.msg_controllen as usize); |
1929 | CMSG_FIRSTHDR(&mhdr as *const msghdr) |
1930 | } else { |
1931 | ptr::null() |
1932 | }.as_ref() |
1933 | }; |
1934 | |
1935 | // Ignore errors if this socket address has statically-known length |
1936 | // |
1937 | // This is to ensure that unix socket addresses have their length set appropriately. |
1938 | let _ = address.set_length(mhdr.msg_namelen as usize); |
1939 | |
1940 | RecvMsg { |
1941 | bytes: r as usize, |
1942 | cmsghdr, |
1943 | address: Some(address), |
1944 | flags: MsgFlags::from_bits_truncate(mhdr.msg_flags), |
1945 | mhdr, |
1946 | iobufs: std::marker::PhantomData, |
1947 | } |
1948 | } |
1949 | |
1950 | /// Pack pointers to various structures into into msghdr |
1951 | /// |
1952 | /// # Safety |
1953 | /// `iov_buffer` and `iov_buffer_len` must point to a slice |
1954 | /// of `IoSliceMut` and number of available elements or be a null pointer and 0 |
1955 | /// |
1956 | /// `cmsg_buffer` and `cmsg_capacity` must point to a byte buffer used |
1957 | /// to store control headers later or be a null pointer and 0 if control |
1958 | /// headers are not used |
1959 | /// |
1960 | /// Buffers must remain valid for the whole lifetime of msghdr |
1961 | unsafe fn pack_mhdr_to_receive<S>( |
1962 | iov_buffer: *mut IoSliceMut, |
1963 | iov_buffer_len: usize, |
1964 | cmsg_buffer: *mut u8, |
1965 | cmsg_capacity: usize, |
1966 | address: *mut S, |
1967 | ) -> msghdr |
1968 | where |
1969 | S: SockaddrLike |
1970 | { |
1971 | // Musl's msghdr has private fields, so this is the only way to |
1972 | // initialize it. |
1973 | let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed(); |
1974 | let p = mhdr.as_mut_ptr(); |
1975 | (*p).msg_name = address as *mut c_void; |
1976 | (*p).msg_namelen = S::size(); |
1977 | (*p).msg_iov = iov_buffer as *mut iovec; |
1978 | (*p).msg_iovlen = iov_buffer_len as _; |
1979 | (*p).msg_control = cmsg_buffer as *mut c_void; |
1980 | (*p).msg_controllen = cmsg_capacity as _; |
1981 | (*p).msg_flags = 0; |
1982 | mhdr.assume_init() |
1983 | } |
1984 | |
1985 | fn pack_mhdr_to_send<'a, I, C, S>( |
1986 | cmsg_buffer: &mut [u8], |
1987 | iov: I, |
1988 | cmsgs: C, |
1989 | addr: Option<&S> |
1990 | ) -> msghdr |
1991 | where |
1992 | I: AsRef<[IoSlice<'a>]>, |
1993 | C: AsRef<[ControlMessage<'a>]>, |
1994 | S: SockaddrLike + 'a |
1995 | { |
1996 | let capacity = cmsg_buffer.len(); |
1997 | |
1998 | // The message header must be initialized before the individual cmsgs. |
1999 | let cmsg_ptr = if capacity > 0 { |
2000 | cmsg_buffer.as_mut_ptr() as *mut c_void |
2001 | } else { |
2002 | ptr::null_mut() |
2003 | }; |
2004 | |
2005 | let mhdr = unsafe { |
2006 | // Musl's msghdr has private fields, so this is the only way to |
2007 | // initialize it. |
2008 | let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed(); |
2009 | let p = mhdr.as_mut_ptr(); |
2010 | (*p).msg_name = addr.map(S::as_ptr).unwrap_or(ptr::null()) as *mut _; |
2011 | (*p).msg_namelen = addr.map(S::len).unwrap_or(0); |
2012 | // transmute iov into a mutable pointer. sendmsg doesn't really mutate |
2013 | // the buffer, but the standard says that it takes a mutable pointer |
2014 | (*p).msg_iov = iov.as_ref().as_ptr() as *mut _; |
2015 | (*p).msg_iovlen = iov.as_ref().len() as _; |
2016 | (*p).msg_control = cmsg_ptr; |
2017 | (*p).msg_controllen = capacity as _; |
2018 | (*p).msg_flags = 0; |
2019 | mhdr.assume_init() |
2020 | }; |
2021 | |
2022 | // Encode each cmsg. This must happen after initializing the header because |
2023 | // CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields. |
2024 | // CMSG_FIRSTHDR is always safe |
2025 | let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(&mhdr as *const msghdr) }; |
2026 | for cmsg in cmsgs.as_ref() { |
2027 | assert_ne!(pmhdr, ptr::null_mut()); |
2028 | // Safe because we know that pmhdr is valid, and we initialized it with |
2029 | // sufficient space |
2030 | unsafe { cmsg.encode_into(pmhdr) }; |
2031 | // Safe because mhdr is valid |
2032 | pmhdr = unsafe { CMSG_NXTHDR(&mhdr as *const msghdr, pmhdr) }; |
2033 | } |
2034 | |
2035 | mhdr |
2036 | } |
2037 | |
2038 | /// Receive message in scatter-gather vectors from a socket, and |
2039 | /// optionally receive ancillary data into the provided buffer. |
2040 | /// If no ancillary data is desired, use () as the type parameter. |
2041 | /// |
2042 | /// # Arguments |
2043 | /// |
2044 | /// * `fd`: Socket file descriptor |
2045 | /// * `iov`: Scatter-gather list of buffers to receive the message |
2046 | /// * `cmsg_buffer`: Space to receive ancillary data. Should be created by |
2047 | /// [`cmsg_space!`](../../macro.cmsg_space.html) |
2048 | /// * `flags`: Optional flags passed directly to the operating system. |
2049 | /// |
2050 | /// # References |
2051 | /// [recvmsg(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html) |
2052 | pub fn recvmsg<'a, 'outer, 'inner, S>(fd: RawFd, iov: &'outer mut [IoSliceMut<'inner>], |
2053 | mut cmsg_buffer: Option<&'a mut Vec<u8>>, |
2054 | flags: MsgFlags) -> Result<RecvMsg<'a, 'outer, S>> |
2055 | where S: SockaddrLike + 'a, |
2056 | 'inner: 'outer |
2057 | { |
2058 | let mut address = mem::MaybeUninit::uninit(); |
2059 | |
2060 | let (msg_control, msg_controllen) = cmsg_buffer.as_mut() |
2061 | .map(|v| (v.as_mut_ptr(), v.capacity())) |
2062 | .unwrap_or((ptr::null_mut(), 0)); |
2063 | let mut mhdr = unsafe { |
2064 | pack_mhdr_to_receive(iov.as_mut().as_mut_ptr(), iov.len(), msg_control, msg_controllen, address.as_mut_ptr()) |
2065 | }; |
2066 | |
2067 | let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) }; |
2068 | |
2069 | let r = Errno::result(ret)?; |
2070 | |
2071 | Ok(unsafe { read_mhdr(mhdr, r, msg_controllen, address.assume_init()) }) |
2072 | } |
2073 | } |
2074 | |
2075 | /// Create an endpoint for communication |
2076 | /// |
2077 | /// The `protocol` specifies a particular protocol to be used with the |
2078 | /// socket. Normally only a single protocol exists to support a |
2079 | /// particular socket type within a given protocol family, in which case |
2080 | /// protocol can be specified as `None`. However, it is possible that many |
2081 | /// protocols may exist, in which case a particular protocol must be |
2082 | /// specified in this manner. |
2083 | /// |
2084 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html) |
2085 | pub fn socket<T: Into<Option<SockProtocol>>>( |
2086 | domain: AddressFamily, |
2087 | ty: SockType, |
2088 | flags: SockFlag, |
2089 | protocol: T, |
2090 | ) -> Result<RawFd> { |
2091 | let protocol: i32 = match protocol.into() { |
2092 | None => 0, |
2093 | Some(p: i32) => p as c_int, |
2094 | }; |
2095 | |
2096 | // SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a |
2097 | // little easier to understand by separating it out. So we have to merge these bitfields |
2098 | // here. |
2099 | let mut ty: i32 = ty as c_int; |
2100 | ty |= flags.bits(); |
2101 | |
2102 | let res: i32 = unsafe { libc::socket(domain as c_int, ty, protocol) }; |
2103 | |
2104 | Errno::result(res) |
2105 | } |
2106 | |
2107 | /// Create a pair of connected sockets |
2108 | /// |
2109 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html) |
2110 | pub fn socketpair<T: Into<Option<SockProtocol>>>( |
2111 | domain: AddressFamily, |
2112 | ty: SockType, |
2113 | protocol: T, |
2114 | flags: SockFlag, |
2115 | ) -> Result<(RawFd, RawFd)> { |
2116 | let protocol: i32 = match protocol.into() { |
2117 | None => 0, |
2118 | Some(p: i32) => p as c_int, |
2119 | }; |
2120 | |
2121 | // SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a |
2122 | // little easier to understand by separating it out. So we have to merge these bitfields |
2123 | // here. |
2124 | let mut ty: i32 = ty as c_int; |
2125 | ty |= flags.bits(); |
2126 | |
2127 | let mut fds: [i32; 2] = [-1, -1]; |
2128 | |
2129 | let res: i32 = unsafe { |
2130 | libc::socketpair(domain as c_int, type_:ty, protocol, socket_vector:fds.as_mut_ptr()) |
2131 | }; |
2132 | Errno::result(res)?; |
2133 | |
2134 | Ok((fds[0], fds[1])) |
2135 | } |
2136 | |
2137 | /// Listen for connections on a socket |
2138 | /// |
2139 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html) |
2140 | pub fn listen(sockfd: RawFd, backlog: usize) -> Result<()> { |
2141 | let res: i32 = unsafe { libc::listen(socket:sockfd, backlog as c_int) }; |
2142 | |
2143 | Errno::result(res).map(op:drop) |
2144 | } |
2145 | |
2146 | /// Bind a name to a socket |
2147 | /// |
2148 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html) |
2149 | pub fn bind(fd: RawFd, addr: &dyn SockaddrLike) -> Result<()> { |
2150 | let res: i32 = unsafe { libc::bind(socket:fd, address:addr.as_ptr(), address_len:addr.len()) }; |
2151 | |
2152 | Errno::result(res).map(op:drop) |
2153 | } |
2154 | |
2155 | /// Accept a connection on a socket |
2156 | /// |
2157 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html) |
2158 | pub fn accept(sockfd: RawFd) -> Result<RawFd> { |
2159 | let res: i32 = unsafe { libc::accept(socket:sockfd, address:ptr::null_mut(), address_len:ptr::null_mut()) }; |
2160 | |
2161 | Errno::result(res) |
2162 | } |
2163 | |
2164 | /// Accept a connection on a socket |
2165 | /// |
2166 | /// [Further reading](https://man7.org/linux/man-pages/man2/accept.2.html) |
2167 | #[cfg (any( |
2168 | all( |
2169 | target_os = "android" , |
2170 | any( |
2171 | target_arch = "aarch64" , |
2172 | target_arch = "x86" , |
2173 | target_arch = "x86_64" |
2174 | ) |
2175 | ), |
2176 | target_os = "dragonfly" , |
2177 | target_os = "emscripten" , |
2178 | target_os = "freebsd" , |
2179 | target_os = "fuchsia" , |
2180 | target_os = "illumos" , |
2181 | target_os = "linux" , |
2182 | target_os = "netbsd" , |
2183 | target_os = "openbsd" |
2184 | ))] |
2185 | pub fn accept4(sockfd: RawFd, flags: SockFlag) -> Result<RawFd> { |
2186 | let res: i32 = unsafe { |
2187 | libc::accept4(fd:sockfd, addr:ptr::null_mut(), len:ptr::null_mut(), flg:flags.bits()) |
2188 | }; |
2189 | |
2190 | Errno::result(res) |
2191 | } |
2192 | |
2193 | /// Initiate a connection on a socket |
2194 | /// |
2195 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html) |
2196 | pub fn connect(fd: RawFd, addr: &dyn SockaddrLike) -> Result<()> { |
2197 | let res: i32 = unsafe { libc::connect(socket:fd, address:addr.as_ptr(), addr.len()) }; |
2198 | |
2199 | Errno::result(res).map(op:drop) |
2200 | } |
2201 | |
2202 | /// Receive data from a connection-oriented socket. Returns the number of |
2203 | /// bytes read |
2204 | /// |
2205 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html) |
2206 | pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: MsgFlags) -> Result<usize> { |
2207 | unsafe { |
2208 | let ret: isize = libc::recv( |
2209 | socket:sockfd, |
2210 | buf:buf.as_mut_ptr() as *mut c_void, |
2211 | buf.len() as size_t, |
2212 | flags:flags.bits(), |
2213 | ); |
2214 | |
2215 | Errno::result(ret).map(|r: isize| r as usize) |
2216 | } |
2217 | } |
2218 | |
2219 | /// Receive data from a connectionless or connection-oriented socket. Returns |
2220 | /// the number of bytes read and, for connectionless sockets, the socket |
2221 | /// address of the sender. |
2222 | /// |
2223 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html) |
2224 | pub fn recvfrom<T: SockaddrLike>( |
2225 | sockfd: RawFd, |
2226 | buf: &mut [u8], |
2227 | ) -> Result<(usize, Option<T>)> { |
2228 | unsafe { |
2229 | let mut addr: MaybeUninit = mem::MaybeUninit::<T>::uninit(); |
2230 | let mut len: u32 = mem::size_of_val(&addr) as socklen_t; |
2231 | |
2232 | let ret: usize = Errno::result(libc::recvfrom( |
2233 | socket:sockfd, |
2234 | buf:buf.as_mut_ptr() as *mut c_void, |
2235 | buf.len() as size_t, |
2236 | flags:0, |
2237 | addr:addr.as_mut_ptr() as *mut libc::sockaddr, |
2238 | &mut len as *mut socklen_t, |
2239 | ))? as usize; |
2240 | |
2241 | Ok(( |
2242 | ret, |
2243 | T::from_raw( |
2244 | addr:addr.assume_init().as_ptr(), |
2245 | len:Some(len), |
2246 | ), |
2247 | )) |
2248 | } |
2249 | } |
2250 | |
2251 | /// Send a message to a socket |
2252 | /// |
2253 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html) |
2254 | pub fn sendto( |
2255 | fd: RawFd, |
2256 | buf: &[u8], |
2257 | addr: &dyn SockaddrLike, |
2258 | flags: MsgFlags, |
2259 | ) -> Result<usize> { |
2260 | let ret: isize = unsafe { |
2261 | libc::sendto( |
2262 | socket:fd, |
2263 | buf:buf.as_ptr() as *const c_void, |
2264 | buf.len() as size_t, |
2265 | flags:flags.bits(), |
2266 | addr:addr.as_ptr(), |
2267 | addrlen:addr.len(), |
2268 | ) |
2269 | }; |
2270 | |
2271 | Errno::result(ret).map(|r: isize| r as usize) |
2272 | } |
2273 | |
2274 | /// Send data to a connection-oriented socket. Returns the number of bytes read |
2275 | /// |
2276 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html) |
2277 | pub fn send(fd: RawFd, buf: &[u8], flags: MsgFlags) -> Result<usize> { |
2278 | let ret: isize = unsafe { |
2279 | libc::send( |
2280 | socket:fd, |
2281 | buf:buf.as_ptr() as *const c_void, |
2282 | buf.len() as size_t, |
2283 | flags:flags.bits(), |
2284 | ) |
2285 | }; |
2286 | |
2287 | Errno::result(ret).map(|r: isize| r as usize) |
2288 | } |
2289 | |
2290 | /* |
2291 | * |
2292 | * ===== Socket Options ===== |
2293 | * |
2294 | */ |
2295 | |
2296 | /// Represents a socket option that can be retrieved. |
2297 | pub trait GetSockOpt: Copy { |
2298 | type Val; |
2299 | |
2300 | /// Look up the value of this socket option on the given socket. |
2301 | fn get(&self, fd: RawFd) -> Result<Self::Val>; |
2302 | } |
2303 | |
2304 | /// Represents a socket option that can be set. |
2305 | pub trait SetSockOpt: Clone { |
2306 | type Val; |
2307 | |
2308 | /// Set the value of this socket option on the given socket. |
2309 | fn set(&self, fd: RawFd, val: &Self::Val) -> Result<()>; |
2310 | } |
2311 | |
2312 | /// Get the current value for the requested socket option |
2313 | /// |
2314 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html) |
2315 | pub fn getsockopt<O: GetSockOpt>(fd: RawFd, opt: O) -> Result<O::Val> { |
2316 | opt.get(fd) |
2317 | } |
2318 | |
2319 | /// Sets the value for the requested socket option |
2320 | /// |
2321 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html) |
2322 | /// |
2323 | /// # Examples |
2324 | /// |
2325 | /// ``` |
2326 | /// use nix::sys::socket::setsockopt; |
2327 | /// use nix::sys::socket::sockopt::KeepAlive; |
2328 | /// use std::net::TcpListener; |
2329 | /// use std::os::unix::io::AsRawFd; |
2330 | /// |
2331 | /// let listener = TcpListener::bind("0.0.0.0:0" ).unwrap(); |
2332 | /// let fd = listener.as_raw_fd(); |
2333 | /// let res = setsockopt(fd, KeepAlive, &true); |
2334 | /// assert!(res.is_ok()); |
2335 | /// ``` |
2336 | pub fn setsockopt<O: SetSockOpt>( |
2337 | fd: RawFd, |
2338 | opt: O, |
2339 | val: &O::Val, |
2340 | ) -> Result<()> { |
2341 | opt.set(fd, val) |
2342 | } |
2343 | |
2344 | /// Get the address of the peer connected to the socket `fd`. |
2345 | /// |
2346 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html) |
2347 | pub fn getpeername<T: SockaddrLike>(fd: RawFd) -> Result<T> { |
2348 | unsafe { |
2349 | let mut addr: MaybeUninit = mem::MaybeUninit::<T>::uninit(); |
2350 | let mut len: u32 = T::size(); |
2351 | |
2352 | let ret: i32 = libc::getpeername( |
2353 | socket:fd, |
2354 | address:addr.as_mut_ptr() as *mut libc::sockaddr, |
2355 | &mut len, |
2356 | ); |
2357 | |
2358 | Errno::result(ret)?; |
2359 | |
2360 | T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(err:Errno::EINVAL) |
2361 | } |
2362 | } |
2363 | |
2364 | /// Get the current address to which the socket `fd` is bound. |
2365 | /// |
2366 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html) |
2367 | pub fn getsockname<T: SockaddrLike>(fd: RawFd) -> Result<T> { |
2368 | unsafe { |
2369 | let mut addr: MaybeUninit = mem::MaybeUninit::<T>::uninit(); |
2370 | let mut len: u32 = T::size(); |
2371 | |
2372 | let ret: i32 = libc::getsockname( |
2373 | socket:fd, |
2374 | address:addr.as_mut_ptr() as *mut libc::sockaddr, |
2375 | &mut len, |
2376 | ); |
2377 | |
2378 | Errno::result(ret)?; |
2379 | |
2380 | T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(err:Errno::EINVAL) |
2381 | } |
2382 | } |
2383 | |
2384 | /// Return the appropriate `SockAddr` type from a `sockaddr_storage` of a |
2385 | /// certain size. |
2386 | /// |
2387 | /// In C this would usually be done by casting. The `len` argument |
2388 | /// should be the number of bytes in the `sockaddr_storage` that are actually |
2389 | /// allocated and valid. It must be at least as large as all the useful parts |
2390 | /// of the structure. Note that in the case of a `sockaddr_un`, `len` need not |
2391 | /// include the terminating null. |
2392 | #[deprecated ( |
2393 | since = "0.24.0" , |
2394 | note = "use SockaddrLike or SockaddrStorage instead" |
2395 | )] |
2396 | #[allow (deprecated)] |
2397 | pub fn sockaddr_storage_to_addr( |
2398 | addr: &sockaddr_storage, |
2399 | len: usize, |
2400 | ) -> Result<SockAddr> { |
2401 | assert!(len <= mem::size_of::<sockaddr_storage>()); |
2402 | if len < mem::size_of_val(&addr.ss_family) { |
2403 | return Err(Errno::ENOTCONN); |
2404 | } |
2405 | |
2406 | match c_int::from(addr.ss_family) { |
2407 | #[cfg (feature = "net" )] |
2408 | libc::AF_INET => { |
2409 | assert!(len >= mem::size_of::<sockaddr_in>()); |
2410 | let sin = unsafe { |
2411 | *(addr as *const sockaddr_storage as *const sockaddr_in) |
2412 | }; |
2413 | Ok(SockAddr::Inet(InetAddr::V4(sin))) |
2414 | } |
2415 | #[cfg (feature = "net" )] |
2416 | libc::AF_INET6 => { |
2417 | assert!(len >= mem::size_of::<sockaddr_in6>()); |
2418 | let sin6 = unsafe { *(addr as *const _ as *const sockaddr_in6) }; |
2419 | Ok(SockAddr::Inet(InetAddr::V6(sin6))) |
2420 | } |
2421 | libc::AF_UNIX => unsafe { |
2422 | let sun = *(addr as *const _ as *const sockaddr_un); |
2423 | let sun_len = len.try_into().unwrap(); |
2424 | Ok(SockAddr::Unix(UnixAddr::from_raw_parts(sun, sun_len))) |
2425 | }, |
2426 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
2427 | #[cfg (feature = "net" )] |
2428 | libc::AF_PACKET => { |
2429 | use libc::sockaddr_ll; |
2430 | // Don't assert anything about the size. |
2431 | // Apparently the Linux kernel can return smaller sizes when |
2432 | // the value in the last element of sockaddr_ll (`sll_addr`) is |
2433 | // smaller than the declared size of that field |
2434 | let sll = unsafe { *(addr as *const _ as *const sockaddr_ll) }; |
2435 | Ok(SockAddr::Link(LinkAddr(sll))) |
2436 | } |
2437 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
2438 | libc::AF_NETLINK => { |
2439 | use libc::sockaddr_nl; |
2440 | let snl = unsafe { *(addr as *const _ as *const sockaddr_nl) }; |
2441 | Ok(SockAddr::Netlink(NetlinkAddr(snl))) |
2442 | } |
2443 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
2444 | libc::AF_ALG => { |
2445 | use libc::sockaddr_alg; |
2446 | let salg = unsafe { *(addr as *const _ as *const sockaddr_alg) }; |
2447 | Ok(SockAddr::Alg(AlgAddr(salg))) |
2448 | } |
2449 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
2450 | libc::AF_VSOCK => { |
2451 | use libc::sockaddr_vm; |
2452 | let svm = unsafe { *(addr as *const _ as *const sockaddr_vm) }; |
2453 | Ok(SockAddr::Vsock(VsockAddr(svm))) |
2454 | } |
2455 | af => panic!("unexpected address family {}" , af), |
2456 | } |
2457 | } |
2458 | |
2459 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
2460 | pub enum Shutdown { |
2461 | /// Further receptions will be disallowed. |
2462 | Read, |
2463 | /// Further transmissions will be disallowed. |
2464 | Write, |
2465 | /// Further receptions and transmissions will be disallowed. |
2466 | Both, |
2467 | } |
2468 | |
2469 | /// Shut down part of a full-duplex connection. |
2470 | /// |
2471 | /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html) |
2472 | pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> { |
2473 | unsafe { |
2474 | use libc::shutdown; |
2475 | |
2476 | let how: i32 = match how { |
2477 | Shutdown::Read => libc::SHUT_RD, |
2478 | Shutdown::Write => libc::SHUT_WR, |
2479 | Shutdown::Both => libc::SHUT_RDWR, |
2480 | }; |
2481 | |
2482 | Errno::result(shutdown(df, how)).map(op:drop) |
2483 | } |
2484 | } |
2485 | |
2486 | #[cfg (test)] |
2487 | mod tests { |
2488 | #[test ] |
2489 | fn can_use_cmsg_space() { |
2490 | let _ = cmsg_space!(u8); |
2491 | } |
2492 | } |
2493 | |