1#![deny(missing_docs, missing_debug_implementations, rust_2018_idioms)]
54#![cfg_attr(docsrs, feature(doc_cfg))]
56#![cfg_attr(test, deny(warnings))]
58#![doc(test(attr(deny(warnings))))]
60
61use std::fmt;
62#[cfg(not(target_os = "redox"))]
63use std::io::IoSlice;
64#[cfg(not(target_os = "redox"))]
65use std::marker::PhantomData;
66#[cfg(not(target_os = "redox"))]
67use std::mem;
68use std::mem::MaybeUninit;
69use std::net::SocketAddr;
70use std::ops::{Deref, DerefMut};
71use std::time::Duration;
72
73macro_rules! impl_debug {
79 (
80 $type: path,
82 $(
83 $(#[$target: meta])*
84 $libc: ident :: $flag: ident
88 ),+ $(,)*
89 ) => {
90 impl std::fmt::Debug for $type {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 let string = match self.0 {
93 $(
94 $(#[$target])*
95 $libc :: $flag => stringify!($flag),
96 )+
97 n => return write!(f, "{n}"),
98 };
99 f.write_str(string)
100 }
101 }
102 };
103}
104
105macro_rules! from {
107 ($from: ty, $for: ty) => {
108 impl From<$from> for $for {
109 fn from(socket: $from) -> $for {
110 #[cfg(unix)]
111 unsafe {
112 <$for>::from_raw_fd(socket.into_raw_fd())
113 }
114 #[cfg(windows)]
115 unsafe {
116 <$for>::from_raw_socket(socket.into_raw_socket())
117 }
118 }
119 }
120 };
121}
122
123#[rustfmt::skip]
125macro_rules! man_links {
126 ($syscall: tt ( $section: tt ) ) => {
128 concat!(
129 man_links!(__ intro),
130 man_links!(__ unix $syscall($section)),
131 man_links!(__ windows $syscall($section)),
132 )
133 };
134 (unix: $syscall: tt ( $section: tt ) ) => {
136 concat!(
137 man_links!(__ intro),
138 man_links!(__ unix $syscall($section)),
139 )
140 };
141 (windows: $syscall: tt ( $section: tt ) ) => {
143 concat!(
144 man_links!(__ intro),
145 man_links!(__ windows $syscall($section)),
146 )
147 };
148 (__ intro) => {
150 "\n\nAdditional documentation can be found in manual of the OS:\n\n"
151 };
152 (__ unix $syscall: tt ( $section: tt ) ) => {
154 concat!(
155 " * DragonFly BSD: <https://man.dragonflybsd.org/?command=", stringify!($syscall), "§ion=", stringify!($section), ">\n",
156 " * FreeBSD: <https://www.freebsd.org/cgi/man.cgi?query=", stringify!($syscall), "&sektion=", stringify!($section), ">\n",
157 " * Linux: <https://man7.org/linux/man-pages/man", stringify!($section), "/", stringify!($syscall), ".", stringify!($section), ".html>\n",
158 " * macOS: <https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/", stringify!($syscall), ".", stringify!($section), ".html> (archived, actually for iOS)\n",
159 " * NetBSD: <https://man.netbsd.org/", stringify!($syscall), ".", stringify!($section), ">\n",
160 " * OpenBSD: <https://man.openbsd.org/", stringify!($syscall), ".", stringify!($section), ">\n",
161 " * iOS: <https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/", stringify!($syscall), ".", stringify!($section), ".html> (archived)\n",
162 " * illumos: <https://illumos.org/man/3SOCKET/", stringify!($syscall), ">\n",
163 )
164 };
165 (__ windows $syscall: tt ( $section: tt ) ) => {
167 concat!(
168 " * Windows: <https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-", stringify!($syscall), ">\n",
169 )
170 };
171}
172
173mod sockaddr;
174mod socket;
175mod sockref;
176
177#[cfg_attr(unix, path = "sys/unix.rs")]
178#[cfg_attr(windows, path = "sys/windows.rs")]
179mod sys;
180
181#[cfg(not(any(windows, unix)))]
182compile_error!("Socket2 doesn't support the compile target");
183
184use sys::c_int;
185
186pub use sockaddr::SockAddr;
187pub use socket::Socket;
188pub use sockref::SockRef;
189
190#[cfg(not(any(
191 target_os = "haiku",
192 target_os = "illumos",
193 target_os = "netbsd",
194 target_os = "redox",
195 target_os = "solaris",
196)))]
197pub use socket::InterfaceIndexOrAddress;
198
199#[derive(Copy, Clone, Eq, PartialEq)]
209pub struct Domain(c_int);
210
211impl Domain {
212 pub const IPV4: Domain = Domain(sys::AF_INET);
214
215 pub const IPV6: Domain = Domain(sys::AF_INET6);
217
218 pub const UNIX: Domain = Domain(sys::AF_UNIX);
220
221 pub const fn for_address(address: SocketAddr) -> Domain {
223 match address {
224 SocketAddr::V4(_) => Domain::IPV4,
225 SocketAddr::V6(_) => Domain::IPV6,
226 }
227 }
228}
229
230impl From<c_int> for Domain {
231 fn from(d: c_int) -> Domain {
232 Domain(d)
233 }
234}
235
236impl From<Domain> for c_int {
237 fn from(d: Domain) -> c_int {
238 d.0
239 }
240}
241
242#[derive(Copy, Clone, Eq, PartialEq)]
252pub struct Type(c_int);
253
254impl Type {
255 pub const STREAM: Type = Type(sys::SOCK_STREAM);
259
260 pub const DGRAM: Type = Type(sys::SOCK_DGRAM);
264
265 #[cfg(all(feature = "all", target_os = "linux"))]
269 #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
270 pub const DCCP: Type = Type(sys::SOCK_DCCP);
271
272 #[cfg(all(feature = "all", not(target_os = "espidf")))]
274 #[cfg_attr(docsrs, doc(cfg(all(feature = "all", not(target_os = "espidf")))))]
275 pub const SEQPACKET: Type = Type(sys::SOCK_SEQPACKET);
276
277 #[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))]
279 #[cfg_attr(
280 docsrs,
281 doc(cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf")))))
282 )]
283 pub const RAW: Type = Type(sys::SOCK_RAW);
284}
285
286impl From<c_int> for Type {
287 fn from(t: c_int) -> Type {
288 Type(t)
289 }
290}
291
292impl From<Type> for c_int {
293 fn from(t: Type) -> c_int {
294 t.0
295 }
296}
297
298#[derive(Copy, Clone, Eq, PartialEq)]
306pub struct Protocol(c_int);
307
308impl Protocol {
309 pub const ICMPV4: Protocol = Protocol(sys::IPPROTO_ICMP);
311
312 pub const ICMPV6: Protocol = Protocol(sys::IPPROTO_ICMPV6);
314
315 pub const TCP: Protocol = Protocol(sys::IPPROTO_TCP);
317
318 pub const UDP: Protocol = Protocol(sys::IPPROTO_UDP);
320
321 #[cfg(target_os = "linux")]
322 pub const MPTCP: Protocol = Protocol(sys::IPPROTO_MPTCP);
324
325 #[cfg(all(feature = "all", target_os = "linux"))]
327 #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))]
328 pub const DCCP: Protocol = Protocol(sys::IPPROTO_DCCP);
329
330 #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
332 pub const SCTP: Protocol = Protocol(sys::IPPROTO_SCTP);
333
334 #[cfg(all(
336 feature = "all",
337 any(
338 target_os = "android",
339 target_os = "freebsd",
340 target_os = "fuchsia",
341 target_os = "linux",
342 )
343 ))]
344 pub const UDPLITE: Protocol = Protocol(sys::IPPROTO_UDPLITE);
345
346 #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "openbsd")))]
348 pub const DIVERT: Protocol = Protocol(sys::IPPROTO_DIVERT);
349}
350
351impl From<c_int> for Protocol {
352 fn from(p: c_int) -> Protocol {
353 Protocol(p)
354 }
355}
356
357impl From<Protocol> for c_int {
358 fn from(p: Protocol) -> c_int {
359 p.0
360 }
361}
362
363#[cfg(not(target_os = "redox"))]
367#[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
368#[derive(Copy, Clone, Eq, PartialEq)]
369pub struct RecvFlags(c_int);
370
371#[cfg(not(target_os = "redox"))]
372impl RecvFlags {
373 #[cfg(not(target_os = "espidf"))]
381 pub const fn is_truncated(self) -> bool {
382 self.0 & sys::MSG_TRUNC != 0
383 }
384}
385
386#[repr(transparent)]
390pub struct MaybeUninitSlice<'a>(sys::MaybeUninitSlice<'a>);
391
392impl<'a> fmt::Debug for MaybeUninitSlice<'a> {
393 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
394 fmt::Debug::fmt(self.0.as_slice(), fmt)
395 }
396}
397
398impl<'a> MaybeUninitSlice<'a> {
399 pub fn new(buf: &'a mut [MaybeUninit<u8>]) -> MaybeUninitSlice<'a> {
405 MaybeUninitSlice(sys::MaybeUninitSlice::new(buf))
406 }
407}
408
409impl<'a> Deref for MaybeUninitSlice<'a> {
410 type Target = [MaybeUninit<u8>];
411
412 fn deref(&self) -> &[MaybeUninit<u8>] {
413 self.0.as_slice()
414 }
415}
416
417impl<'a> DerefMut for MaybeUninitSlice<'a> {
418 fn deref_mut(&mut self) -> &mut [MaybeUninit<u8>] {
419 self.0.as_mut_slice()
420 }
421}
422
423#[derive(Debug, Clone)]
427pub struct TcpKeepalive {
428 #[cfg_attr(
429 any(target_os = "openbsd", target_os = "haiku", target_os = "vita"),
430 allow(dead_code)
431 )]
432 time: Option<Duration>,
433 #[cfg(not(any(
434 target_os = "openbsd",
435 target_os = "redox",
436 target_os = "solaris",
437 target_os = "nto",
438 target_os = "espidf",
439 target_os = "vita",
440 target_os = "haiku",
441 )))]
442 interval: Option<Duration>,
443 #[cfg(not(any(
444 target_os = "openbsd",
445 target_os = "redox",
446 target_os = "solaris",
447 target_os = "windows",
448 target_os = "nto",
449 target_os = "espidf",
450 target_os = "vita",
451 target_os = "haiku",
452 )))]
453 retries: Option<u32>,
454}
455
456impl TcpKeepalive {
457 #[allow(clippy::new_without_default)]
459 pub const fn new() -> TcpKeepalive {
460 TcpKeepalive {
461 time: None,
462 #[cfg(not(any(
463 target_os = "openbsd",
464 target_os = "redox",
465 target_os = "solaris",
466 target_os = "nto",
467 target_os = "espidf",
468 target_os = "vita",
469 target_os = "haiku",
470 )))]
471 interval: None,
472 #[cfg(not(any(
473 target_os = "openbsd",
474 target_os = "redox",
475 target_os = "solaris",
476 target_os = "windows",
477 target_os = "nto",
478 target_os = "espidf",
479 target_os = "vita",
480 target_os = "haiku",
481 )))]
482 retries: None,
483 }
484 }
485
486 pub const fn with_time(self, time: Duration) -> Self {
498 Self {
499 time: Some(time),
500 ..self
501 }
502 }
503
504 #[cfg(any(
512 target_os = "android",
513 target_os = "dragonfly",
514 target_os = "freebsd",
515 target_os = "fuchsia",
516 target_os = "illumos",
517 target_os = "ios",
518 target_os = "visionos",
519 target_os = "linux",
520 target_os = "macos",
521 target_os = "netbsd",
522 target_os = "tvos",
523 target_os = "watchos",
524 target_os = "windows",
525 target_os = "cygwin",
526 ))]
527 #[cfg_attr(
528 docsrs,
529 doc(cfg(any(
530 target_os = "android",
531 target_os = "dragonfly",
532 target_os = "freebsd",
533 target_os = "fuchsia",
534 target_os = "illumos",
535 target_os = "ios",
536 target_os = "visionos",
537 target_os = "linux",
538 target_os = "macos",
539 target_os = "netbsd",
540 target_os = "tvos",
541 target_os = "watchos",
542 target_os = "windows",
543 )))
544 )]
545 pub const fn with_interval(self, interval: Duration) -> Self {
546 Self {
547 interval: Some(interval),
548 ..self
549 }
550 }
551
552 #[cfg(all(
557 feature = "all",
558 any(
559 target_os = "android",
560 target_os = "dragonfly",
561 target_os = "freebsd",
562 target_os = "fuchsia",
563 target_os = "illumos",
564 target_os = "ios",
565 target_os = "visionos",
566 target_os = "linux",
567 target_os = "macos",
568 target_os = "netbsd",
569 target_os = "tvos",
570 target_os = "watchos",
571 target_os = "cygwin",
572 )
573 ))]
574 #[cfg_attr(
575 docsrs,
576 doc(cfg(all(
577 feature = "all",
578 any(
579 target_os = "android",
580 target_os = "dragonfly",
581 target_os = "freebsd",
582 target_os = "fuchsia",
583 target_os = "illumos",
584 target_os = "ios",
585 target_os = "visionos",
586 target_os = "linux",
587 target_os = "macos",
588 target_os = "netbsd",
589 target_os = "tvos",
590 target_os = "watchos",
591 )
592 )))
593 )]
594 pub const fn with_retries(self, retries: u32) -> Self {
595 Self {
596 retries: Some(retries),
597 ..self
598 }
599 }
600}
601
602#[cfg(not(target_os = "redox"))]
607pub struct MsgHdr<'addr, 'bufs, 'control> {
608 inner: sys::msghdr,
609 #[allow(clippy::type_complexity)]
610 _lifetimes: PhantomData<(&'addr SockAddr, &'bufs IoSlice<'bufs>, &'control [u8])>,
611}
612
613#[cfg(not(target_os = "redox"))]
614impl<'addr, 'bufs, 'control> MsgHdr<'addr, 'bufs, 'control> {
615 #[allow(clippy::new_without_default)]
617 pub fn new() -> MsgHdr<'addr, 'bufs, 'control> {
618 MsgHdr {
620 inner: unsafe { mem::zeroed() },
621 _lifetimes: PhantomData,
622 }
623 }
624
625 pub fn with_addr(mut self, addr: &'addr SockAddr) -> Self {
630 sys::set_msghdr_name(&mut self.inner, addr);
631 self
632 }
633
634 pub fn with_buffers(mut self, bufs: &'bufs [IoSlice<'_>]) -> Self {
639 let ptr = bufs.as_ptr() as *mut _;
640 sys::set_msghdr_iov(&mut self.inner, ptr, bufs.len());
641 self
642 }
643
644 pub fn with_control(mut self, buf: &'control [u8]) -> Self {
649 let ptr = buf.as_ptr() as *mut _;
650 sys::set_msghdr_control(&mut self.inner, ptr, buf.len());
651 self
652 }
653
654 pub fn with_flags(mut self, flags: sys::c_int) -> Self {
658 sys::set_msghdr_flags(&mut self.inner, flags);
659 self
660 }
661}
662
663#[cfg(not(target_os = "redox"))]
664impl<'name, 'bufs, 'control> fmt::Debug for MsgHdr<'name, 'bufs, 'control> {
665 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
666 "MsgHdr".fmt(fmt)
667 }
668}
669
670#[cfg(not(target_os = "redox"))]
675pub struct MsgHdrMut<'addr, 'bufs, 'control> {
676 inner: sys::msghdr,
677 #[allow(clippy::type_complexity)]
678 _lifetimes: PhantomData<(
679 &'addr mut SockAddr,
680 &'bufs mut MaybeUninitSlice<'bufs>,
681 &'control mut [u8],
682 )>,
683}
684
685#[cfg(not(target_os = "redox"))]
686impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> {
687 #[allow(clippy::new_without_default)]
689 pub fn new() -> MsgHdrMut<'addr, 'bufs, 'control> {
690 MsgHdrMut {
692 inner: unsafe { mem::zeroed() },
693 _lifetimes: PhantomData,
694 }
695 }
696
697 #[allow(clippy::needless_pass_by_ref_mut)]
702 pub fn with_addr(mut self, addr: &'addr mut SockAddr) -> Self {
703 sys::set_msghdr_name(&mut self.inner, addr);
704 self
705 }
706
707 pub fn with_buffers(mut self, bufs: &'bufs mut [MaybeUninitSlice<'_>]) -> Self {
712 sys::set_msghdr_iov(&mut self.inner, bufs.as_mut_ptr().cast(), bufs.len());
713 self
714 }
715
716 pub fn with_control(mut self, buf: &'control mut [MaybeUninit<u8>]) -> Self {
721 sys::set_msghdr_control(&mut self.inner, buf.as_mut_ptr().cast(), buf.len());
722 self
723 }
724
725 pub fn flags(&self) -> RecvFlags {
727 sys::msghdr_flags(&self.inner)
728 }
729
730 pub fn control_len(&self) -> usize {
736 sys::msghdr_control_len(&self.inner)
737 }
738}
739
740#[cfg(not(target_os = "redox"))]
741impl<'name, 'bufs, 'control> fmt::Debug for MsgHdrMut<'name, 'bufs, 'control> {
742 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
743 "MsgHdrMut".fmt(fmt)
744 }
745}