s2n_quic_platform/socket/
options.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::syscall;
5use s2n_quic_core::inet::SocketAddress;
6use std::{
7    io,
8    net::{SocketAddr, TcpListener, UdpSocket},
9};
10
11#[derive(Clone, Copy, Debug, Default)]
12pub enum ReusePort {
13    #[default]
14    Disabled,
15    /// Enables reuse port before binding the socket
16    ///
17    /// NOTE: the provided `addr` must not be bound to a random port (`0`)
18    BeforeBind,
19    /// Enables reuse port after binding the socket
20    AfterBind,
21}
22
23#[derive(Clone, Debug)]
24#[non_exhaustive]
25pub struct Options {
26    pub addr: SocketAddr,
27    pub reuse_address: bool,
28    pub reuse_port: ReusePort,
29    pub gro: bool,
30    pub blocking: bool,
31    pub delay: bool,
32    pub send_buffer: Option<usize>,
33    pub recv_buffer: Option<usize>,
34    pub backlog: usize,
35    pub only_v6: bool,
36}
37
38impl Default for Options {
39    #[inline]
40    fn default() -> Self {
41        Self {
42            addr: SocketAddress::default().into(),
43            reuse_address: false,
44            reuse_port: Default::default(),
45            gro: true,
46            blocking: false,
47            send_buffer: None,
48            recv_buffer: None,
49            delay: false,
50            backlog: 4096,
51            only_v6: false,
52        }
53    }
54}
55
56impl Options {
57    #[inline]
58    pub fn new(addr: SocketAddr) -> Self {
59        Self {
60            addr,
61            ..Default::default()
62        }
63    }
64
65    #[inline]
66    pub fn build_udp(&self) -> io::Result<UdpSocket> {
67        let socket = syscall::udp_socket(self.addr, self.only_v6)?;
68
69        if self.gro {
70            let _ = syscall::configure_gro(&socket);
71        }
72
73        let _ = syscall::configure_tos(&socket);
74        let _ = syscall::configure_mtu_disc(&socket);
75
76        self.build_common(&socket)?;
77
78        let socket = socket.into();
79        Ok(socket)
80    }
81
82    #[inline]
83    pub fn build_tcp_listener(&self) -> io::Result<TcpListener> {
84        let domain = socket2::Domain::for_address(self.addr);
85        let ty = socket2::Type::STREAM;
86        let protocol = socket2::Protocol::TCP;
87
88        let socket = socket2::Socket::new(domain, ty, Some(protocol))?;
89
90        socket.set_tcp_nodelay(!self.delay)?;
91
92        self.build_common(&socket)?;
93
94        socket.listen(self.backlog.try_into().unwrap_or(core::ffi::c_int::MAX))?;
95
96        Ok(socket.into())
97    }
98
99    fn build_common(&self, socket: &socket2::Socket) -> io::Result<()> {
100        socket.set_reuse_address(self.reuse_address)?;
101        socket.set_nonblocking(!self.blocking)?;
102
103        if let Some(send_buffer) = self.send_buffer {
104            let _ = socket.set_send_buffer_size(send_buffer);
105        }
106
107        if let Some(recv_buffer) = self.recv_buffer {
108            let _ = socket.set_recv_buffer_size(recv_buffer);
109        }
110
111        if let ReusePort::BeforeBind = self.reuse_port {
112            assert_ne!(self.addr.port(), 0);
113            set_reuse_port(socket)?;
114        }
115
116        socket.bind(&self.addr.into())?;
117
118        if let ReusePort::AfterBind = self.reuse_port {
119            set_reuse_port(socket)?;
120        }
121
122        Ok(())
123    }
124}
125
126#[cfg(windows)]
127fn set_reuse_port(_socket: &socket2::Socket) -> io::Result<()> {
128    Err(io::Error::new(
129        io::ErrorKind::InvalidInput,
130        "reuse port is not supported on windows",
131    ))
132}
133
134#[cfg(not(windows))]
135fn set_reuse_port(socket: &socket2::Socket) -> io::Result<()> {
136    socket.set_reuse_port(true)
137}