cap_net_ext/
lib.rs

1//! Extension traits for `TcpListener`, `UdpSocket`, and `Pool`.
2//!
3//! cap-std's [`TcpListener`], following the Rust standard library
4//! `TcpListener`, combines the `socket`, `bind`, `listen`, and `connect`
5//! operations of the POSIX socket API into a single `bind` or `connect`
6//! operation. In some use cases, it's desirable to perform the steps
7//! separately.
8//!
9//! This API adds extension traits to cap-std's `TcpListener`, `UdpSocket`,
10//! and `Pool` which support the following sequence for accepting incoming
11//! connections:
12//!
13//!  - [`TcpListenerExt::new`] performs a `socket` and returns a new
14//!    `TcpListener` that is not yet bound.
15//!  - [`Pool::bind_existing_tcp_listener`] performs a `bind`, checking that
16//!    the address is in the `Pool`.
17//!  - [`TcpListenerExt::listen`] performs a `listen`.
18//!  - Then, the regular [`TcpListener::accept`] may be used to accept new
19//!    connections. Alternatively, [`TcpListener::accept_with`] may be used.
20//!
21//! and the following sequence for initiating outgoing connections:
22//!
23//!  - [`TcpListenerExt::new`] performs a `socket` and returns a new
24//!    `TcpListener` that is not yet connected.
25//!  - [`Pool::connect_into_tcp_stream`] performs a `connect`, checking that
26//!    the address is in the `Pool`.
27//!
28//! [`TcpListenerExt::new`] and [`TcpListener::accept_with`] additionally
29//! have [`Blocking`] arguments for requesting non-blocking operation.
30//!
31//! Similar API adaptations are available for UDP sockets as well.
32
33#![deny(missing_docs)]
34#![forbid(unsafe_code)]
35#![doc(
36    html_logo_url = "https://raw.githubusercontent.com/bytecodealliance/cap-std/main/media/cap-std.svg"
37)]
38#![doc(
39    html_favicon_url = "https://raw.githubusercontent.com/bytecodealliance/cap-std/main/media/cap-std.ico"
40)]
41
42use cap_primitives::net::no_socket_addrs;
43use cap_std::net::{IpAddr, Pool, SocketAddr, TcpListener, TcpStream, ToSocketAddrs, UdpSocket};
44use rustix::fd::OwnedFd;
45use std::io;
46
47/// Address families supported by [`TcpListenerExt::new`] and
48/// [`UdpSocketExt::new`].
49#[derive(Copy, Clone, Debug, Eq, PartialEq)]
50pub enum AddressFamily {
51    /// IPv4
52    Ipv4,
53
54    /// IPv6
55    Ipv6,
56}
57
58impl AddressFamily {
59    /// Return the `AddressFamily` of an IP address.
60    pub fn of_ip_addr(ip_addr: IpAddr) -> Self {
61        match ip_addr {
62            IpAddr::V4(_) => AddressFamily::Ipv4,
63            IpAddr::V6(_) => AddressFamily::Ipv6,
64        }
65    }
66
67    /// Return the `AddressFamily` of a socket address.
68    pub fn of_socket_addr(socket_addr: SocketAddr) -> Self {
69        match socket_addr {
70            SocketAddr::V4(_) => AddressFamily::Ipv4,
71            SocketAddr::V6(_) => AddressFamily::Ipv6,
72        }
73    }
74}
75
76impl From<AddressFamily> for rustix::net::AddressFamily {
77    fn from(address_family: AddressFamily) -> Self {
78        match address_family {
79            AddressFamily::Ipv4 => rustix::net::AddressFamily::INET,
80            AddressFamily::Ipv6 => rustix::net::AddressFamily::INET6,
81        }
82    }
83}
84
85/// Select blocking or non-blocking mode.
86#[derive(Copy, Clone, Debug, Eq, PartialEq)]
87pub enum Blocking {
88    /// Non-blocking
89    No,
90
91    /// Blocking
92    Yes,
93}
94
95/// A trait for extending `TcpListener` types.
96pub trait TcpListenerExt: private::Sealed + Sized {
97    /// Creates a new TCP socket with the given address family.
98    ///
99    /// The created socket is not bound or connected to any address and may be
100    /// used for either listening or connecting. Use
101    /// [`PoolExt::bind_existing_tcp_listener`] to bind it in preparation for
102    /// listening, or [`PoolExt::connect_into_tcp_stream`] to initiate a
103    /// connection.
104    ///
105    /// This is similar to [`Pool::bind_tcp_listener`] in that it creates a TCP
106    /// socket, however it does not perform the `bind` or `listen` steps. And,
107    /// it has a `blocking` argument to select blocking or non-blocking mode
108    /// for the created socket.
109    ///
110    /// And it's similar to [`Pool::connect_tcp_stream`] in that it creates a
111    /// TCP socket, however it does not perform the `connect` step. And, it has
112    /// a `blocking` argument to select blocking or non-blocking mode for the
113    /// created socket.
114    fn new(address_family: AddressFamily, blocking: Blocking) -> io::Result<Self>;
115
116    /// Enble listening in a `TcpListener`.
117    ///
118    /// A newly-created [`TcpListener`] created with [`TcpListenerExt::new`]
119    /// and bound with [`PoolExt::bind_existing_tcp_listener`] is not yet
120    /// listening; this function enables listening. After this, the listener
121    /// may accept new connections with [`accept`] or [`accept_with`].
122    ///
123    /// This is similar to [`Pool::bind_tcp_listener`] in that it performs the
124    /// `listen` step, however it does not create the socket itself, or bind
125    /// it.
126    ///
127    /// The `backlog` argument specifies an optional hint to the implementation
128    /// about how many connections can be waiting before new connections are
129    /// refused or ignored.
130    ///
131    /// [`accept`]: TcpListener::accept
132    /// [`accept_with`]: TcpListenerExt::accept_with
133    fn listen(&self, backlog: Option<i32>) -> io::Result<()>;
134
135    /// Similar to [`accept`], but the resulting TCP connection are optionally
136    /// set to non-blocking mode.
137    ///
138    /// The `accept` call itself may still block, if the socket is in blocking
139    /// mode.
140    ///
141    /// [`accept`]: TcpListener::accept
142    fn accept_with(&self, blocking: Blocking) -> io::Result<(TcpStream, SocketAddr)>;
143}
144
145impl TcpListenerExt for TcpListener {
146    fn new(address_family: AddressFamily, blocking: Blocking) -> io::Result<Self> {
147        socket(address_family, blocking, rustix::net::SocketType::STREAM).map(Self::from)
148    }
149
150    fn listen(&self, backlog: Option<i32>) -> io::Result<()> {
151        let backlog = backlog.unwrap_or_else(default_backlog);
152
153        Ok(rustix::net::listen(self, backlog)?)
154    }
155
156    fn accept_with(&self, blocking: Blocking) -> io::Result<(TcpStream, SocketAddr)> {
157        let (stream, addr) = rustix::net::acceptfrom_with(self, socket_flags(blocking))?;
158        set_socket_flags(&stream, blocking)?;
159
160        // We know have a TCP socket, so we know we'll get an IP address.
161        let addr = match addr {
162            Some(rustix::net::SocketAddrAny::V4(v4)) => SocketAddr::V4(v4),
163            Some(rustix::net::SocketAddrAny::V6(v6)) => SocketAddr::V6(v6),
164            _ => unreachable!(),
165        };
166
167        Ok((TcpStream::from(stream), addr))
168    }
169}
170
171/// A trait for extending `UdpSocket` types.
172pub trait UdpSocketExt: private::Sealed + Sized {
173    /// Creates a new `UdpSocket` with the given address family.
174    ///
175    /// The created socket is initially not bound or connected to any address.
176    /// Use [`PoolExt::bind_existing_udp_socket`] to bind it, or
177    /// [`PoolExt::connect_existing_udp_socket`] to initiate a connection.
178    ///
179    /// This is similar to [`Pool::bind_udp_socket`] in that it creates a UDP
180    /// socket, however it does not perform the `bind`. And, it has a
181    /// `blocking` argument to select blocking or non-blocking mode for the
182    /// created socket.
183    ///
184    /// And it's similar to [`Pool::connect_udp_socket`] in that it creates a
185    /// UDP socket, however it does not perform the `connect` step. And, it has
186    /// a `blocking` argument to select blocking or non-blocking mode for the
187    /// created socket.
188    fn new(address_family: AddressFamily, blocking: Blocking) -> io::Result<Self>;
189}
190
191impl UdpSocketExt for UdpSocket {
192    fn new(address_family: AddressFamily, blocking: Blocking) -> io::Result<Self> {
193        socket(address_family, blocking, rustix::net::SocketType::DGRAM).map(Self::from)
194    }
195}
196
197/// A trait for extending `Pool` types.
198///
199/// These functions have a `ToSocketAddrs` argument, which can return either
200/// IPv4 or IPv6 addresses, however they also require the socket to be created
201/// with a specific address family up front. Consequently, it's recommended to
202/// do address resolution outside of this API and just pass resolved
203/// `SocketAddr`s in.
204pub trait PoolExt: private::Sealed {
205    /// Bind a [`TcpListener`] to the specified address.
206    ///
207    /// A newly-created `TcpListener` created with [`TcpListenerExt::new`]
208    /// has not been bound yet; this function binds it. Before it can accept
209    /// connections, it must be marked for listening with
210    /// [`TcpListenerExt::listen`].
211    ///
212    /// This is similar to [`Pool::bind_tcp_listener`] in that it binds a TCP
213    /// socket, however it does not create the socket itself, or perform the
214    /// `listen` step.
215    ///
216    /// This function ensures that the address to be bound is permitted by the
217    /// pool, and performs the bind. To perform these steps separately, create
218    /// a [`TcpBinder`] with [`Self::tcp_binder`] and use
219    /// [`TcpBinder::bind_existing_tcp_listener`].
220    fn bind_existing_tcp_listener<A: ToSocketAddrs>(
221        &self,
222        listener: &TcpListener,
223        addrs: A,
224    ) -> io::Result<()>;
225
226    /// Bind a [`UdpSocket`] to the specified address.
227    ///
228    /// A newly-created `UdpSocket` created with [`UdpSocketExt::new`] has not
229    /// been bound yet; this function binds it.
230    ///
231    /// This is similar to [`Pool::bind_udp_socket`] in that it binds a UDP
232    /// socket, however it does not create the socket itself.
233    ///
234    /// This function ensures that the address to be bound is permitted by the
235    /// pool, and performs the bind. To perform these steps separately, create
236    /// a [`UdpBinder`] with [`Self::udp_binder`] and use
237    /// [`UdpBinder::bind_existing_udp_socket`].
238    fn bind_existing_udp_socket<A: ToSocketAddrs>(
239        &self,
240        socket: &UdpSocket,
241        addrs: A,
242    ) -> io::Result<()>;
243
244    /// Initiate a TCP connection, converting a [`TcpListener`] to a
245    /// [`TcpStream`].
246    ///
247    /// This is simlar to [`Pool::connect_tcp_stream`] in that it performs a
248    /// TCP connection, but instead of creating a new socket itself it takes a
249    /// [`TcpListener`], such as one created with [`TcpListenerExt::new`].
250    ///
251    /// Despite the name, this function uses the `TcpListener` type as a
252    /// generic socket container.
253    ///
254    /// This function ensures that the address to connect to is permitted by
255    /// the pool, and performs the connect. To perform these steps separately,
256    /// create a [`TcpConnecter`] with [`Self::tcp_connecter`] and use
257    /// [`TcpConnecter::connect_into_tcp_stream`].
258    fn connect_into_tcp_stream<A: ToSocketAddrs>(
259        &self,
260        socket: TcpListener,
261        addrs: A,
262    ) -> io::Result<TcpStream>;
263
264    /// Initiate a TCP connection on a socket.
265    ///
266    /// This is simlar to [`Self::connect_into_tcp_stream`], however instead of
267    /// converting a `TcpListener` to a `TcpStream`, it leaves fd in the
268    /// existing `TcpListener`.
269    ///
270    /// This function ensures that the address to connect to is permitted by
271    /// the pool, and performs the connect. To perform these steps separately,
272    /// create a [`TcpConnecter`] with [`Self::tcp_connecter`] and use
273    /// [`TcpConnecter::connect_existing_tcp_listener`].
274    fn connect_existing_tcp_listener<A: ToSocketAddrs>(
275        &self,
276        socket: &TcpListener,
277        addrs: A,
278    ) -> io::Result<()>;
279
280    /// Initiate a UDP connection.
281    ///
282    /// This is simlar to [`Pool::connect_udp_socket`] in that it performs a
283    /// UDP connection, but instead of creating a new socket itself it takes a
284    /// [`UdpSocket`], such as one created with [`UdpSocketExt::new`].
285    ///
286    /// This function ensures that the address to connect to is permitted by
287    /// the pool, and performs the connect. To perform these steps separately,
288    /// create a [`UdpConnecter`] with [`Self::udp_connecter`] and use
289    /// [`UdpConnecter::connect_existing_udp_socket`].
290    fn connect_existing_udp_socket<A: ToSocketAddrs>(
291        &self,
292        socket: &UdpSocket,
293        addrs: A,
294    ) -> io::Result<()>;
295
296    /// Create a TCP binder.
297    ///
298    /// This is an alternative to [`Self::bind_existing_tcp_listener`]. It
299    /// checks that all the addresses in `addrs` are permitted for TCP binding
300    /// up front, and then records them in a [`TcpBinder`] which can then be
301    /// used to make repeated [`TcpBinder::bind_existing_tcp_listener`] calls.
302    fn tcp_binder<A: ToSocketAddrs>(&self, addrs: A) -> io::Result<TcpBinder>;
303
304    /// Create a UDP binder.
305    ///
306    /// This is an alternative to [`Self::bind_existing_udp_socket`]. It checks
307    /// that all the addresses in `addrs` are permitted for UDP binding up
308    /// front, and then records them in a [`UdpBinder`] which can then be used
309    /// to make repeated [`UdpBinder::bind_existing_udp_socket`] calls.
310    fn udp_binder<A: ToSocketAddrs>(&self, addrs: A) -> io::Result<UdpBinder>;
311
312    /// Create a TCP connecter.
313    ///
314    /// This is an alternative to [`Self::connect_into_tcp_stream`] and
315    /// [`Self::connect_existing_tcp_listener`]. It checks that all the
316    /// addresses in `addrs` are permitted for TCP connecting up front, and
317    /// then records them in a [`TcpConnecter`] which can then be used to make
318    /// repeated [`TcpConnecter::connect_into_tcp_stream`] and
319    /// [`TcpConnecter::connect_existing_tcp_listener`] calls.
320    fn tcp_connecter<A: ToSocketAddrs>(&self, addrs: A) -> io::Result<TcpConnecter>;
321
322    /// Create a UDP connecter.
323    ///
324    /// This is an alternative to [`Self::connect_existing_udp_socket`]. It
325    /// checks that all the addresses in `addrs` are permitted for UDP
326    /// connecting up front, and then records them in a [`UdpConnecter`] which
327    /// can then be used to make repeated
328    /// [`UdpConnecter::connect_existing_udp_socket`] calls.
329    fn udp_connecter<A: ToSocketAddrs>(&self, addrs: A) -> io::Result<UdpConnecter>;
330}
331
332impl PoolExt for Pool {
333    fn bind_existing_tcp_listener<A: ToSocketAddrs>(
334        &self,
335        listener: &TcpListener,
336        addrs: A,
337    ) -> io::Result<()> {
338        let addrs = addrs.to_socket_addrs()?;
339
340        let mut last_err = None;
341        for addr in addrs {
342            self._pool().check_addr(&addr)?;
343
344            set_reuseaddr(listener)?;
345
346            match rustix::net::bind(listener, &addr) {
347                Ok(()) => return Ok(()),
348                Err(err) => last_err = Some(err.into()),
349            }
350        }
351        match last_err {
352            Some(err) => Err(err),
353            None => Err(no_socket_addrs()),
354        }
355    }
356
357    fn bind_existing_udp_socket<A: ToSocketAddrs>(
358        &self,
359        socket: &UdpSocket,
360        addrs: A,
361    ) -> io::Result<()> {
362        let addrs = addrs.to_socket_addrs()?;
363
364        let mut last_err = None;
365        for addr in addrs {
366            self._pool().check_addr(&addr)?;
367
368            match rustix::net::bind(socket, &addr) {
369                Ok(()) => return Ok(()),
370                Err(err) => last_err = Some(err),
371            }
372        }
373        match last_err {
374            Some(err) => Err(err.into()),
375            None => Err(no_socket_addrs()),
376        }
377    }
378
379    fn connect_into_tcp_stream<A: ToSocketAddrs>(
380        &self,
381        socket: TcpListener,
382        addrs: A,
383    ) -> io::Result<TcpStream> {
384        self.connect_existing_tcp_listener(&socket, addrs)?;
385        Ok(TcpStream::from(OwnedFd::from(socket)))
386    }
387
388    fn connect_existing_tcp_listener<A: ToSocketAddrs>(
389        &self,
390        socket: &TcpListener,
391        addrs: A,
392    ) -> io::Result<()> {
393        let addrs = addrs.to_socket_addrs()?;
394
395        let mut last_err = None;
396        for addr in addrs {
397            self._pool().check_addr(&addr)?;
398
399            match rustix::net::connect(socket, &addr) {
400                Ok(()) => return Ok(()),
401                Err(err) => last_err = Some(err),
402            }
403        }
404        match last_err {
405            Some(err) => Err(err.into()),
406            None => Err(no_socket_addrs()),
407        }
408    }
409
410    fn connect_existing_udp_socket<A: ToSocketAddrs>(
411        &self,
412        socket: &UdpSocket,
413        addrs: A,
414    ) -> io::Result<()> {
415        let addrs = addrs.to_socket_addrs()?;
416
417        let mut last_err = None;
418        for addr in addrs {
419            self._pool().check_addr(&addr)?;
420
421            match rustix::net::connect(socket, &addr) {
422                Ok(()) => return Ok(()),
423                Err(err) => last_err = Some(err),
424            }
425        }
426        match last_err {
427            Some(err) => Err(err.into()),
428            None => Err(no_socket_addrs()),
429        }
430    }
431
432    fn tcp_binder<A: ToSocketAddrs>(&self, addrs: A) -> io::Result<TcpBinder> {
433        Ok(TcpBinder(check_addrs(self._pool(), addrs)?))
434    }
435
436    fn udp_binder<A: ToSocketAddrs>(&self, addrs: A) -> io::Result<UdpBinder> {
437        Ok(UdpBinder(check_addrs(self._pool(), addrs)?))
438    }
439
440    fn tcp_connecter<A: ToSocketAddrs>(&self, addrs: A) -> io::Result<TcpConnecter> {
441        Ok(TcpConnecter(check_addrs(self._pool(), addrs)?))
442    }
443
444    fn udp_connecter<A: ToSocketAddrs>(&self, addrs: A) -> io::Result<UdpConnecter> {
445        Ok(UdpConnecter(check_addrs(self._pool(), addrs)?))
446    }
447}
448
449/// Check all the addresses in `addrs` and return a new list of them.
450fn check_addrs<A: ToSocketAddrs>(
451    pool: &cap_primitives::net::Pool,
452    addrs: A,
453) -> io::Result<smallvec::SmallVec<[SocketAddr; 1]>> {
454    let mut checked = smallvec::SmallVec::new();
455    for addr in addrs.to_socket_addrs()? {
456        pool.check_addr(&addr)?;
457        checked.push(addr);
458    }
459    Ok(checked)
460}
461
462/// A utility for binding TCP listeners.
463///
464/// See [`PoolExt::tcp_binder`] for details.
465pub struct TcpBinder(smallvec::SmallVec<[SocketAddr; 1]>);
466
467impl TcpBinder {
468    /// Bind a [`TcpListener`].
469    ///
470    /// A newly-created `TcpListener` created with [`TcpListenerExt::new`]
471    /// has not been bound yet; this function binds it. Before it can accept
472    /// connections, it must be marked for listening with
473    /// [`TcpListenerExt::listen`].
474    ///
475    /// This is similar to [`Pool::bind_tcp_listener`] in that it binds a TCP
476    /// socket, however it does not create the socket itself, or perform the
477    /// `listen` step.
478    ///
479    /// This is similar to [`PoolExt::bind_existing_tcp_listener`] except that
480    /// it uses a `TcpBinder` which contains addresses that have already been
481    /// checked against a `Pool`.
482    pub fn bind_existing_tcp_listener(&self, listener: &TcpListener) -> io::Result<()> {
483        let mut last_err = None;
484        for addr in &self.0 {
485            set_reuseaddr(listener)?;
486
487            match rustix::net::bind(listener, addr) {
488                Ok(()) => return Ok(()),
489                Err(err) => last_err = Some(err.into()),
490            }
491        }
492        match last_err {
493            Some(err) => Err(err),
494            None => Err(no_socket_addrs()),
495        }
496    }
497}
498
499/// A utility for binding UDP sockets.
500///
501/// See [`PoolExt::udp_binder`] for details.
502pub struct UdpBinder(smallvec::SmallVec<[SocketAddr; 1]>);
503
504impl UdpBinder {
505    /// Bind a [`UdpSocket`] to the specified address.
506    ///
507    /// A newly-created `UdpSocket` created with [`UdpSocketExt::new`] has not
508    /// been bound yet; this function binds it.
509    ///
510    /// This is similar to [`Pool::bind_udp_socket`] in that it binds a UDP
511    /// socket, however it does not create the socket itself.
512    ///
513    /// This is similar to [`PoolExt::bind_existing_udp_socket`] except that
514    /// it uses a `UdpBinder` which contains addresses that have already been
515    /// checked against a `Pool`.
516    pub fn bind_existing_udp_socket(&self, socket: &UdpSocket) -> io::Result<()> {
517        let mut last_err = None;
518        for addr in &self.0 {
519            match rustix::net::bind(socket, addr) {
520                Ok(()) => return Ok(()),
521                Err(err) => last_err = Some(err.into()),
522            }
523        }
524        match last_err {
525            Some(err) => Err(err),
526            None => Err(no_socket_addrs()),
527        }
528    }
529}
530
531/// A utility for making TCP connections.
532///
533/// See [`PoolExt::tcp_connecter`] for details.
534pub struct TcpConnecter(smallvec::SmallVec<[SocketAddr; 1]>);
535
536impl TcpConnecter {
537    /// Initiate a TCP connection, converting a [`TcpListener`] to a
538    /// [`TcpStream`].
539    ///
540    /// This is simlar to [`Pool::connect_tcp_stream`] in that it performs a
541    /// TCP connection, but instead of creating a new socket itself it takes a
542    /// [`TcpListener`], such as one created with [`TcpListenerExt::new`].
543    ///
544    /// Despite the name, this function uses the `TcpListener` type as a
545    /// generic socket container.
546    ///
547    /// This is similar to [`PoolExt::connect_into_tcp_stream`] except that
548    /// it uses a `TcpConnecter` which contains addresses that have already
549    /// been checked against a `Pool`.
550    pub fn connect_into_tcp_stream(&self, socket: TcpListener) -> io::Result<TcpStream> {
551        self.connect_existing_tcp_listener(&socket)?;
552        Ok(TcpStream::from(OwnedFd::from(socket)))
553    }
554
555    /// Initiate a TCP connection on a socket.
556    ///
557    /// This is simlar to [`Pool::connect_into_tcp_stream`], however instead of
558    /// converting a `TcpListener` to a `TcpStream`, it leaves fd in the
559    /// existing `TcpListener`.
560    ///
561    /// This is similar to [`PoolExt::connect_existing_tcp_listener`] except
562    /// that it uses a `TcpConnecter` which contains addresses that have
563    /// already been checked against a `Pool`.
564    pub fn connect_existing_tcp_listener(&self, socket: &TcpListener) -> io::Result<()> {
565        let mut last_err = None;
566        for addr in &self.0 {
567            match rustix::net::connect(socket, addr) {
568                Ok(()) => return Ok(()),
569                Err(err) => last_err = Some(err),
570            }
571        }
572        match last_err {
573            Some(err) => Err(err.into()),
574            None => Err(no_socket_addrs()),
575        }
576    }
577}
578
579/// A utility for making UDP connections.
580///
581/// See [`PoolExt::udp_connecter`] for details.
582pub struct UdpConnecter(smallvec::SmallVec<[SocketAddr; 1]>);
583
584impl UdpConnecter {
585    /// Initiate a UDP connection.
586    ///
587    /// This is simlar to [`Pool::connect_udp_socket`] in that it performs a
588    /// UDP connection, but instead of creating a new socket itself it takes a
589    /// [`UdpSocket`], such as one created with [`UdpSocketExt::new`].
590    ///
591    /// This is similar to [`PoolExt::connect_existing_udp_socket`] except that
592    /// it uses a `UdpConnecter` which contains addresses that have already
593    /// been checked against a `Pool`.
594    pub fn connect_existing_udp_socket(&self, socket: &UdpSocket) -> io::Result<()> {
595        let mut last_err = None;
596        for addr in &self.0 {
597            match rustix::net::connect(socket, addr) {
598                Ok(()) => return Ok(()),
599                Err(err) => last_err = Some(err),
600            }
601        }
602        match last_err {
603            Some(err) => Err(err.into()),
604            None => Err(no_socket_addrs()),
605        }
606    }
607}
608
609fn socket(
610    address_family: AddressFamily,
611    blocking: Blocking,
612    socket_type: rustix::net::SocketType,
613) -> io::Result<OwnedFd> {
614    // The Rust standard library has code to call `WSAStartup`, which is needed
615    // on Windows before we do any other Winsock2 calls, so just make a useless
616    // API call once.
617    #[cfg(windows)]
618    {
619        use std::sync::Once;
620        static START: Once = Once::new();
621        START.call_once(|| {
622            std::net::TcpStream::connect(std::net::SocketAddrV4::new(
623                std::net::Ipv4Addr::UNSPECIFIED,
624                0,
625            ))
626            .unwrap_err();
627        });
628    }
629
630    // Create the socket, using the desired flags if we can.
631    let socket = rustix::net::socket_with(
632        address_family.into(),
633        socket_type,
634        socket_flags(blocking),
635        None,
636    )?;
637
638    // Set the desired flags if we couldn't set them at creation.
639    set_socket_flags(&socket, blocking)?;
640
641    Ok(socket)
642}
643
644/// Compute flags to pass to socket calls.
645fn socket_flags(blocking: Blocking) -> rustix::net::SocketFlags {
646    let _ = blocking;
647
648    #[allow(unused_mut)]
649    let mut socket_flags = rustix::net::SocketFlags::empty();
650
651    // On platforms which do support `SOCK_CLOEXEC`, use it.
652    #[cfg(not(any(
653        windows,
654        target_os = "macos",
655        target_os = "ios",
656        target_os = "tvos",
657        target_os = "watchos",
658        target_os = "haiku"
659    )))]
660    {
661        socket_flags |= rustix::net::SocketFlags::CLOEXEC;
662    }
663
664    // On platforms which do support `SOCK_NONBLOCK`, use it.
665    #[cfg(not(any(
666        windows,
667        target_os = "macos",
668        target_os = "ios",
669        target_os = "tvos",
670        target_os = "watchos",
671        target_os = "haiku"
672    )))]
673    match blocking {
674        Blocking::Yes => (),
675        Blocking::No => socket_flags |= rustix::net::SocketFlags::NONBLOCK,
676    }
677
678    socket_flags
679}
680
681/// On platforms which don't support `SOCK_CLOEXEC` or `SOCK_NONBLOCK, set them
682/// after creating the socket.
683fn set_socket_flags(fd: &OwnedFd, blocking: Blocking) -> io::Result<()> {
684    let _ = fd;
685    let _ = blocking;
686
687    #[cfg(any(
688        target_os = "macos",
689        target_os = "ios",
690        target_os = "tvos",
691        target_os = "watchos"
692    ))]
693    {
694        rustix::io::ioctl_fioclex(fd)?;
695    }
696
697    #[cfg(any(
698        windows,
699        target_os = "macos",
700        target_os = "ios",
701        target_os = "tvos",
702        target_os = "watchos"
703    ))]
704    match blocking {
705        Blocking::Yes => (),
706        Blocking::No => rustix::io::ioctl_fionbio(fd, true)?,
707    }
708
709    #[cfg(target_os = "haiku")]
710    {
711        let mut flags = rustix::fs::fcntl_getfd(fd)?;
712        flags |= rustix::fs::OFlags::CLOEXEC;
713        match blocking {
714            Blocking::Yes => (),
715            Blocking::No => flags |= rustix::fs::OFlags::NONBLOCK,
716        }
717        rustix::fs::fcntl_setfd(fd, flags)?;
718    }
719
720    Ok(())
721}
722
723/// On platforms where it's desirable, set the `SO_REUSEADDR` option.
724fn set_reuseaddr(listener: &TcpListener) -> io::Result<()> {
725    let _ = listener;
726
727    // The following logic is from
728    // <https://github.com/rust-lang/rust/blob/master/library/std/src/sys_common/net.rs>
729    // at revision defa2456246a8272ceace9c1cdccdf2e4c36175e.
730
731    // On platforms with Berkeley-derived sockets, this allows to quickly
732    // rebind a socket, without needing to wait for the OS to clean up the
733    // previous one.
734    //
735    // On Windows, this allows rebinding sockets which are actively in use,
736    // which allows “socket hijacking”, so we explicitly don't set it here.
737    // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse
738    #[cfg(not(windows))]
739    rustix::net::sockopt::set_socket_reuseaddr(listener, true)?;
740
741    Ok(())
742}
743
744/// Determine the platform-specific default backlog value.
745fn default_backlog() -> i32 {
746    // The following logic is from
747    // <https://github.com/rust-lang/rust/blob/master/library/std/src/sys_common/net.rs>
748    // at revision defa2456246a8272ceace9c1cdccdf2e4c36175e.
749
750    // The 3DS doesn't support a big connection backlog. Sometimes
751    // it allows up to about 37, but other times it doesn't even
752    // accept 32. There may be a global limitation causing this.
753    #[cfg(target_os = "horizon")]
754    let backlog = 20;
755
756    // The default for all other platforms
757    #[cfg(not(target_os = "horizon"))]
758    let backlog = 128;
759
760    backlog
761}
762
763/// Seal the public traits for [future-proofing].
764///
765/// [future-proofing]: https://rust-lang.github.io/api-guidelines/future-proofing.html
766mod private {
767    pub trait Sealed {}
768    impl Sealed for super::TcpListener {}
769    impl Sealed for super::UdpSocket {}
770    impl Sealed for super::Pool {}
771}