Skip to main content

nuclei/net/
sockopt.rs

1//! The options a socket takes before it binds, the same for a TCP socket to set up
2//! ([`TcpSocket`](super::TcpSocket)) and a UDP one ([`UdpSocketBuilder`](super::UdpSocketBuilder)):
3//! one macro writes them for both, so the two never disagree.
4
5/// A network interface's name, as `bind_device` takes it: up to 16 bytes,
6/// held in place.
7#[cfg(any(target_os = "linux", target_os = "android"))]
8#[derive(Clone, Copy, PartialEq, Eq)]
9pub struct DeviceName {
10    pub(super) bytes: [u8; libc::IFNAMSIZ],
11    pub(super) len: u8,
12}
13
14#[cfg(any(target_os = "linux", target_os = "android"))]
15impl core::ops::Deref for DeviceName {
16    type Target = [u8];
17
18    fn deref(&self) -> &[u8] {
19        &self.bytes[..usize::from(self.len)]
20    }
21}
22
23#[cfg(any(target_os = "linux", target_os = "android"))]
24impl core::fmt::Debug for DeviceName {
25    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
26        write!(f, "{:?}", String::from_utf8_lossy(self))
27    }
28}
29
30/// The options of [`sockopt`](self) on `$ty`, a type with a `raw(&self) -> Raw`.
31macro_rules! socket_options {
32    ($($ty:ty),* $(,)?) => {$(
33        impl $ty {
34            /// Sets `SO_REUSEADDR`: the socket may bind an address still held by a
35            /// connection that is closing.
36            pub fn set_reuseaddr(&self, reuseaddr: bool) -> std::io::Result<()> {
37                opt::set_flag(self.raw(), opt::SOL_SOCKET, opt::SO_REUSEADDR, reuseaddr)
38            }
39
40            /// Whether `SO_REUSEADDR` is set.
41            pub fn reuseaddr(&self) -> std::io::Result<bool> {
42                opt::flag(self.raw(), opt::SOL_SOCKET, opt::SO_REUSEADDR)
43            }
44
45            /// Sets `SO_REUSEPORT`: several sockets may bind the same address and
46            /// port, and the system shares what comes among them.
47            #[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))]
48            pub fn set_reuseport(&self, reuseport: bool) -> std::io::Result<()> {
49                opt::set_flag(self.raw(), opt::SOL_SOCKET, opt::SO_REUSEPORT, reuseport)
50            }
51
52            /// Whether `SO_REUSEPORT` is set.
53            #[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))]
54            pub fn reuseport(&self) -> std::io::Result<bool> {
55                opt::flag(self.raw(), opt::SOL_SOCKET, opt::SO_REUSEPORT)
56            }
57
58            /// Sets the size of the system's send buffer (`SO_SNDBUF`); the system
59            /// may round it (Linux doubles it).
60            pub fn set_send_buffer_size(&self, size: u32) -> std::io::Result<()> {
61                opt::set_size(self.raw(), opt::SOL_SOCKET, opt::SO_SNDBUF, size)
62            }
63
64            /// The size of the system's send buffer (`SO_SNDBUF`).
65            pub fn send_buffer_size(&self) -> std::io::Result<u32> {
66                opt::size(self.raw(), opt::SOL_SOCKET, opt::SO_SNDBUF)
67            }
68
69            /// Sets the size of the system's receive buffer (`SO_RCVBUF`); the
70            /// system may round it (Linux doubles it).
71            pub fn set_recv_buffer_size(&self, size: u32) -> std::io::Result<()> {
72                opt::set_size(self.raw(), opt::SOL_SOCKET, opt::SO_RCVBUF, size)
73            }
74
75            /// The size of the system's receive buffer (`SO_RCVBUF`).
76            pub fn recv_buffer_size(&self) -> std::io::Result<u32> {
77                opt::size(self.raw(), opt::SOL_SOCKET, opt::SO_RCVBUF)
78            }
79
80            /// Sets the type of service of the socket's IPv4 packets (`IP_TOS`).
81            pub fn set_tos(&self, tos: u32) -> std::io::Result<()> {
82                let tos = opt::int(u64::from(tos), "type of service")?;
83                opt::set(self.raw(), opt::IPPROTO_IP, opt::IP_TOS, tos)
84            }
85
86            /// The type of service of the socket's IPv4 packets (`IP_TOS`).
87            pub fn tos(&self) -> std::io::Result<u32> {
88                opt::size(self.raw(), opt::IPPROTO_IP, opt::IP_TOS)
89            }
90
91            /// Sets the traffic class of the socket's IPv6 packets (`IPV6_TCLASS`).
92            #[cfg(unix)]
93            pub fn set_tclass_v6(&self, tclass: u32) -> std::io::Result<()> {
94                let tclass = opt::int(u64::from(tclass), "traffic class")?;
95                opt::set(self.raw(), opt::IPPROTO_IPV6, opt::IPV6_TCLASS, tclass)
96            }
97
98            /// The traffic class of the socket's IPv6 packets (`IPV6_TCLASS`).
99            #[cfg(unix)]
100            pub fn tclass_v6(&self) -> std::io::Result<u32> {
101                opt::size(self.raw(), opt::IPPROTO_IPV6, opt::IPV6_TCLASS)
102            }
103
104            /// Sets `IPV6_V6ONLY` on an IPv6 socket: with `true` it takes IPv6 alone,
105            /// else IPv4 too, through addresses mapped into IPv6.
106            ///
107            /// # Errors
108            ///
109            /// As `setsockopt(2)`: an IPv4 socket has no such option, and a bound one
110            /// may no longer change it.
111            pub fn set_only_v6(&self, only_v6: bool) -> std::io::Result<()> {
112                opt::set_flag(self.raw(), opt::IPPROTO_IPV6, opt::IPV6_V6ONLY, only_v6)
113            }
114
115            /// Whether `IPV6_V6ONLY` is set.
116            ///
117            /// # Errors
118            ///
119            /// As `getsockopt(2)`: an IPv4 socket has no such option.
120            pub fn only_v6(&self) -> std::io::Result<bool> {
121                opt::flag(self.raw(), opt::IPPROTO_IPV6, opt::IPV6_V6ONLY)
122            }
123
124            /// Binds the socket to the network interface named `interface`
125            /// (`SO_BINDTODEVICE`), or, with `None`, to none.
126            #[cfg(any(target_os = "linux", target_os = "android"))]
127            pub fn bind_device(&self, interface: Option<&[u8]>) -> std::io::Result<()> {
128                let name = interface.unwrap_or_default();
129                // SAFETY: the option takes the name's bytes as they are.
130                unsafe {
131                    opt::set_raw(
132                        self.raw(),
133                        opt::SOL_SOCKET,
134                        libc::SO_BINDTODEVICE,
135                        name.as_ptr(),
136                        name.len(),
137                    )
138                }
139            }
140
141            /// The network interface the socket is bound to, if any
142            /// (`SO_BINDTODEVICE`).
143            #[cfg(any(target_os = "linux", target_os = "android"))]
144            pub fn device(&self) -> std::io::Result<Option<super::DeviceName>> {
145                let mut name = super::DeviceName {
146                    bytes: [0; libc::IFNAMSIZ],
147                    len: 0,
148                };
149                // SAFETY: the kernel writes at most the buffer's length.
150                let len = unsafe {
151                    opt::get_raw(
152                        self.raw(),
153                        opt::SOL_SOCKET,
154                        libc::SO_BINDTODEVICE,
155                        name.bytes.as_mut_ptr(),
156                        name.bytes.len(),
157                    )?
158                };
159                // The kernel counts the name's terminating zero.
160                let len = name.bytes[..len.min(name.bytes.len())]
161                    .iter()
162                    .position(|&b| b == 0)
163                    .unwrap_or(len.min(name.bytes.len()));
164                name.len = len as u8;
165                Ok((len > 0).then_some(name))
166            }
167        }
168    )*};
169}
170
171pub(super) use socket_options;