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    ))]
526    #[cfg_attr(
527        docsrs,
528        doc(cfg(any(
529            target_os = "android",
530            target_os = "dragonfly",
531            target_os = "freebsd",
532            target_os = "fuchsia",
533            target_os = "illumos",
534            target_os = "ios",
535            target_os = "visionos",
536            target_os = "linux",
537            target_os = "macos",
538            target_os = "netbsd",
539            target_os = "tvos",
540            target_os = "watchos",
541            target_os = "windows",
542        )))
543    )]
544    pub const fn with_interval(self, interval: Duration) -> Self {
545        Self {
546            interval: Some(interval),
547            ..self
548        }
549    }
550
551    #[cfg(all(
556        feature = "all",
557        any(
558            target_os = "android",
559            target_os = "dragonfly",
560            target_os = "freebsd",
561            target_os = "fuchsia",
562            target_os = "illumos",
563            target_os = "ios",
564            target_os = "visionos",
565            target_os = "linux",
566            target_os = "macos",
567            target_os = "netbsd",
568            target_os = "tvos",
569            target_os = "watchos",
570        )
571    ))]
572    #[cfg_attr(
573        docsrs,
574        doc(cfg(all(
575            feature = "all",
576            any(
577                target_os = "android",
578                target_os = "dragonfly",
579                target_os = "freebsd",
580                target_os = "fuchsia",
581                target_os = "illumos",
582                target_os = "ios",
583                target_os = "visionos",
584                target_os = "linux",
585                target_os = "macos",
586                target_os = "netbsd",
587                target_os = "tvos",
588                target_os = "watchos",
589            )
590        )))
591    )]
592    pub const fn with_retries(self, retries: u32) -> Self {
593        Self {
594            retries: Some(retries),
595            ..self
596        }
597    }
598}
599
600#[cfg(not(target_os = "redox"))]
605pub struct MsgHdr<'addr, 'bufs, 'control> {
606    inner: sys::msghdr,
607    #[allow(clippy::type_complexity)]
608    _lifetimes: PhantomData<(&'addr SockAddr, &'bufs IoSlice<'bufs>, &'control [u8])>,
609}
610
611#[cfg(not(target_os = "redox"))]
612impl<'addr, 'bufs, 'control> MsgHdr<'addr, 'bufs, 'control> {
613    #[allow(clippy::new_without_default)]
615    pub fn new() -> MsgHdr<'addr, 'bufs, 'control> {
616        MsgHdr {
618            inner: unsafe { mem::zeroed() },
619            _lifetimes: PhantomData,
620        }
621    }
622
623    pub fn with_addr(mut self, addr: &'addr SockAddr) -> Self {
628        sys::set_msghdr_name(&mut self.inner, addr);
629        self
630    }
631
632    pub fn with_buffers(mut self, bufs: &'bufs [IoSlice<'_>]) -> Self {
637        let ptr = bufs.as_ptr() as *mut _;
638        sys::set_msghdr_iov(&mut self.inner, ptr, bufs.len());
639        self
640    }
641
642    pub fn with_control(mut self, buf: &'control [u8]) -> Self {
647        let ptr = buf.as_ptr() as *mut _;
648        sys::set_msghdr_control(&mut self.inner, ptr, buf.len());
649        self
650    }
651
652    pub fn with_flags(mut self, flags: sys::c_int) -> Self {
656        sys::set_msghdr_flags(&mut self.inner, flags);
657        self
658    }
659}
660
661#[cfg(not(target_os = "redox"))]
662impl<'name, 'bufs, 'control> fmt::Debug for MsgHdr<'name, 'bufs, 'control> {
663    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
664        "MsgHdr".fmt(fmt)
665    }
666}
667
668#[cfg(not(target_os = "redox"))]
673pub struct MsgHdrMut<'addr, 'bufs, 'control> {
674    inner: sys::msghdr,
675    #[allow(clippy::type_complexity)]
676    _lifetimes: PhantomData<(
677        &'addr mut SockAddr,
678        &'bufs mut MaybeUninitSlice<'bufs>,
679        &'control mut [u8],
680    )>,
681}
682
683#[cfg(not(target_os = "redox"))]
684impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> {
685    #[allow(clippy::new_without_default)]
687    pub fn new() -> MsgHdrMut<'addr, 'bufs, 'control> {
688        MsgHdrMut {
690            inner: unsafe { mem::zeroed() },
691            _lifetimes: PhantomData,
692        }
693    }
694
695    #[allow(clippy::needless_pass_by_ref_mut)]
700    pub fn with_addr(mut self, addr: &'addr mut SockAddr) -> Self {
701        sys::set_msghdr_name(&mut self.inner, addr);
702        self
703    }
704
705    pub fn with_buffers(mut self, bufs: &'bufs mut [MaybeUninitSlice<'_>]) -> Self {
710        sys::set_msghdr_iov(&mut self.inner, bufs.as_mut_ptr().cast(), bufs.len());
711        self
712    }
713
714    pub fn with_control(mut self, buf: &'control mut [MaybeUninit<u8>]) -> Self {
719        sys::set_msghdr_control(&mut self.inner, buf.as_mut_ptr().cast(), buf.len());
720        self
721    }
722
723    pub fn flags(&self) -> RecvFlags {
725        sys::msghdr_flags(&self.inner)
726    }
727
728    pub fn control_len(&self) -> usize {
734        sys::msghdr_control_len(&self.inner)
735    }
736}
737
738#[cfg(not(target_os = "redox"))]
739impl<'name, 'bufs, 'control> fmt::Debug for MsgHdrMut<'name, 'bufs, 'control> {
740    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
741        "MsgHdrMut".fmt(fmt)
742    }
743}