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}